summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-09-19 15:21:20 +0800
committerluwenpeng <[email protected]>2023-09-20 10:43:38 +0800
commitae80f71eba09d4381bb070d87bcd2aa6173166c6 (patch)
tree15a9adedfffa31717d8b27837fc13801ff0285d5
parentcb674f9e168b6e709136e17a5bc87d3925c6f479 (diff)
[feature] Support GREv0/GREv1 Decode
-rw-r--r--src/main.rs6
-rw-r--r--src/packet/error.rs11
-rw-r--r--src/packet/packet.rs328
-rw-r--r--src/protocol/ethernet.rs2
-rw-r--r--src/protocol/grev0.rs334
-rw-r--r--src/protocol/grev1.rs186
-rw-r--r--src/protocol/ip.rs2
-rw-r--r--src/protocol/mod.rs4
8 files changed, 872 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs
index 81ef6f4..154a6fe 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -39,6 +39,12 @@ fn trigger_packet_event(
Encapsulation::Ipv6(_, _) => {
queue.add(Event::Ipv6Event, session.clone());
}
+ Encapsulation::Grev0(_, _) => {
+ // TODO
+ }
+ Encapsulation::Grev1(_, _) => {
+ // TODO
+ }
Encapsulation::Tcp(_, _) => {
queue.add(Event::TcpEvent, session.clone());
}
diff --git a/src/packet/error.rs b/src/packet/error.rs
index b6b054b..a9e39a1 100644
--- a/src/packet/error.rs
+++ b/src/packet/error.rs
@@ -18,6 +18,12 @@ pub enum PacketError {
UnsupportIPProtocol,
+ // L3.5
+ IncompleteGreHeader,
+ IncompleteGrev0Header,
+ IncompleteGrev1Header,
+ UnsupportGreVersion,
+
// L4
IncompleteUdpHeader,
IncompleteTcpHeader,
@@ -48,6 +54,11 @@ impl core::fmt::Display for PacketError {
PacketError::InvalidIpv4HeaderLength => write!(f, "Invalid IPv4 Header Length"),
PacketError::InvalidIpv6HeaderLength => write!(f, "Invalid IPv6 Header Length"),
PacketError::UnsupportIPProtocol => write!(f, "Unsupport IP Protocol"),
+ // L3.5
+ PacketError::IncompleteGreHeader => write!(f, "Incomplete GRE Header"),
+ PacketError::IncompleteGrev0Header => write!(f, "Incomplete GREv0 Header"),
+ PacketError::IncompleteGrev1Header => write!(f, "Incomplete GREv1 Header"),
+ PacketError::UnsupportGreVersion => write!(f, "Unsupport GRE Version"),
// L4
PacketError::IncompleteUdpHeader => write!(f, "Incomplete UDP Header"),
PacketError::IncompleteTcpHeader => write!(f, "Incomplete TCP Header"),
diff --git a/src/packet/packet.rs b/src/packet/packet.rs
index 6ab0a71..cc9e2d7 100644
--- a/src/packet/packet.rs
+++ b/src/packet/packet.rs
@@ -2,6 +2,8 @@ use crate::packet::error::PacketError;
use crate::protocol::codec::Decode;
use crate::protocol::ethernet::EtherType;
use crate::protocol::ethernet::EthernetFrame;
+use crate::protocol::grev0::Grev0Header;
+use crate::protocol::grev1::Grev1Header;
use crate::protocol::gtpv1::Gtpv1Header;
use crate::protocol::icmp::IcmpHeader;
use crate::protocol::icmpv6::Icmpv6Header;
@@ -27,6 +29,9 @@ pub enum Encapsulation<'a> {
Ipv4(Ipv4Header, &'a [u8]),
Ipv6(Ipv6Header, &'a [u8]),
+ Grev0(Grev0Header, &'a [u8]),
+ Grev1(Grev1Header, &'a [u8]),
+
Tcp(TcpHeader, &'a [u8]),
Udp(UdpHeader, &'a [u8]),
Icmp(IcmpHeader, &'a [u8]),
@@ -598,6 +603,49 @@ fn handle_l2tp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packe
}
}
+fn handle_gre<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> {
+ if input.len() < 2 {
+ return Err(PacketError::IncompleteGreHeader);
+ }
+
+ let version = input[1] & 0x07;
+ match version {
+ 0 => {
+ let result = Grev0Header::decode(input);
+ if let Ok((payload, header)) = result {
+ dbg!(&header);
+
+ let next_proto = header.protocol_type;
+ packet
+ .encapsulation
+ .push(Encapsulation::Grev0(header, payload));
+
+ return handle_l3(packet, payload, next_proto);
+ } else {
+ return Err(PacketError::IncompleteGrev0Header);
+ }
+ }
+ 1 => {
+ let result = Grev1Header::decode(input);
+ if let Ok((payload, header)) = result {
+ dbg!(&header);
+
+ let next_proto = header.protocol_type;
+ packet
+ .encapsulation
+ .push(Encapsulation::Grev1(header, payload));
+
+ return handle_l3(packet, payload, next_proto);
+ } else {
+ return Err(PacketError::IncompleteGrev1Header);
+ }
+ }
+ _ => {
+ return Err(PacketError::UnsupportGreVersion);
+ }
+ }
+}
+
fn handle_l3<'a>(
packet: &mut Packet<'a>,
input: &'a [u8],
@@ -621,6 +669,7 @@ fn handle_l4<'a>(
next_proto: IPProtocol,
) -> Result<(), PacketError> {
match next_proto {
+ IPProtocol::GRE => handle_gre(packet, input),
IPProtocol::IPINIP => handle_ipv4(packet, input),
IPProtocol::IPV6 => handle_ipv6(packet, input),
IPProtocol::ICMP => handle_icmp(packet, input),
@@ -644,6 +693,8 @@ mod tests {
use crate::protocol::ethernet::EtherType;
use crate::protocol::ethernet::EthernetFrame;
use crate::protocol::ethernet::MacAddress;
+ use crate::protocol::grev0::Grev0Header;
+ use crate::protocol::grev1::Grev1Header;
use crate::protocol::gtpv1::Gtpv1ExtensionHeader;
use crate::protocol::gtpv1::Gtpv1Header;
use crate::protocol::gtpv1::Gtpv1Option;
@@ -2678,4 +2729,281 @@ mod tests {
// assert_eq!(1, 0);
}
+
+ #[test]
+ fn test_packet_handle_eth_ipv4_grev0_ipv4_grev0_ipv4_icmp() {
+ /*
+ * Frame 1: 162 bytes on wire (1296 bits), 162 bytes captured (1296 bits)
+ * Encapsulation type: Ethernet (1)
+ * Arrival Time: Jul 5, 2012 05:13:44.204043000 CST
+ * [Time shift for this packet: 0.000000000 seconds]
+ * Epoch Time: 1341436424.204043000 seconds
+ * [Time delta from previous captured frame: 0.000000000 seconds]
+ * [Time delta from previous displayed frame: 0.000000000 seconds]
+ * [Time since reference or first frame: 0.000000000 seconds]
+ * Frame Number: 1
+ * Frame Length: 162 bytes (1296 bits)
+ * Capture Length: 162 bytes (1296 bits)
+ * [Frame is marked: False]
+ * [Frame is ignored: False]
+ * [Protocols in frame: eth:ethertype:ip:gre:ip:gre:ip:icmp:data]
+ * [Coloring Rule Name: ICMP]
+ * [Coloring Rule String: icmp || icmpv6]
+ * Ethernet II, Src: G-ProCom_dd:22:42 (00:0f:fe:dd:22:42), Dst: Cisco_ff:54:d9 (00:1b:d5:ff:54:d9)
+ * Destination: Cisco_ff:54:d9 (00:1b:d5:ff:54:d9)
+ * Address: Cisco_ff:54:d9 (00:1b:d5:ff:54:d9)
+ * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ * .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ * Source: G-ProCom_dd:22:42 (00:0f:fe:dd:22:42)
+ * Address: G-ProCom_dd:22:42 (00:0f:fe:dd:22:42)
+ * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ * .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ * Type: IPv4 (0x0800)
+ * Internet Protocol Version 4, Src: 72.205.54.70, Dst: 86.106.164.150
+ * 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: 148
+ * Identification: 0x0000 (0)
+ * 010. .... = Flags: 0x2, Don't fragment
+ * 0... .... = Reserved bit: Not set
+ * .1.. .... = Don't fragment: Set
+ * ..0. .... = More fragments: Not set
+ * ...0 0000 0000 0000 = Fragment Offset: 0
+ * Time to Live: 255
+ * Protocol: Generic Routing Encapsulation (47)
+ * Header Checksum: 0x0127 [correct]
+ * [Header checksum status: Good]
+ * [Calculated Checksum: 0x0127]
+ * Source Address: 72.205.54.70
+ * Destination Address: 86.106.164.150
+ * Generic Routing Encapsulation (IP)
+ * Flags and Version: 0x0000
+ * 0... .... .... .... = Checksum Bit: No
+ * .0.. .... .... .... = Routing Bit: No
+ * ..0. .... .... .... = Key Bit: No
+ * ...0 .... .... .... = Sequence Number Bit: No
+ * .... 0... .... .... = Strict Source Route Bit: No
+ * .... .000 .... .... = Recursion control: 0
+ * .... .... 0000 0... = Flags (Reserved): 0
+ * .... .... .... .000 = Version: GRE (0)
+ * Protocol Type: IP (0x0800)
+ * Internet Protocol Version 4, Src: 10.10.11.2, Dst: 10.10.13.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: 124
+ * Identification: 0x27e0 (10208)
+ * 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: 254
+ * Protocol: Generic Routing Encapsulation (47)
+ * Header Checksum: 0x685b [correct]
+ * [Header checksum status: Good]
+ * [Calculated Checksum: 0x685b]
+ * Source Address: 10.10.11.2
+ * Destination Address: 10.10.13.2
+ * Generic Routing Encapsulation (IP)
+ * Flags and Version: 0x0000
+ * 0... .... .... .... = Checksum Bit: No
+ * .0.. .... .... .... = Routing Bit: No
+ * ..0. .... .... .... = Key Bit: No
+ * ...0 .... .... .... = Sequence Number Bit: No
+ * .... 0... .... .... = Strict Source Route Bit: No
+ * .... .000 .... .... = Recursion control: 0
+ * .... .... 0000 0... = Flags (Reserved): 0
+ * .... .... .... .000 = Version: GRE (0)
+ * Protocol Type: IP (0x0800)
+ * Internet Protocol Version 4, Src: 10.10.25.1, Dst: 192.168.1.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: 100
+ * Identification: 0x23b8 (9144)
+ * 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: 255
+ * Protocol: ICMP (1)
+ * Header Checksum: 0xb32b [correct]
+ * [Header checksum status: Good]
+ * [Calculated Checksum: 0xb32b]
+ * Source Address: 10.10.25.1
+ * Destination Address: 192.168.1.2
+ * Internet Control Message Protocol
+ * Type: 8 (Echo (ping) request)
+ * Code: 0
+ * Checksum: 0xcbca [correct]
+ * [Checksum Status: Good]
+ * Identifier (BE): 23 (0x0017)
+ * Identifier (LE): 5888 (0x1700)
+ * Sequence Number (BE): 7666 (0x1df2)
+ * Sequence Number (LE): 61981 (0xf21d)
+ * [Response frame: 2]
+ * Data (72 bytes)
+ * Data: 0000000004129064abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd…
+ * [Length: 72]
+ */
+
+ let bytes = [
+ 0x00, 0x1b, 0xd5, 0xff, 0x54, 0xd9, 0x00, 0x0f, 0xfe, 0xdd, 0x22, 0x42, 0x08, 0x00,
+ 0x45, 0x00, 0x00, 0x94, 0x00, 0x00, 0x40, 0x00, 0xff, 0x2f, 0x01, 0x27, 0x48, 0xcd,
+ 0x36, 0x46, 0x56, 0x6a, 0xa4, 0x96, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x7c,
+ 0x27, 0xe0, 0x00, 0x00, 0xfe, 0x2f, 0x68, 0x5b, 0x0a, 0x0a, 0x0b, 0x02, 0x0a, 0x0a,
+ 0x0d, 0x02, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x64, 0x23, 0xb8, 0x00, 0x00,
+ 0xff, 0x01, 0xb3, 0x2b, 0x0a, 0x0a, 0x19, 0x01, 0xc0, 0xa8, 0x01, 0x02, 0x08, 0x00,
+ 0xcb, 0xca, 0x00, 0x17, 0x1d, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x04, 0x12, 0x90, 0x64,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
+ ];
+
+ 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(), 7);
+ assert_eq!(
+ packet.encapsulation[0],
+ Encapsulation::Eth(
+ EthernetFrame {
+ source_mac: MacAddress([0x00, 0x0f, 0xfe, 0xdd, 0x22, 0x42]),
+ dest_mac: MacAddress([0x00, 0x1b, 0xd5, 0xff, 0x54, 0xd9]),
+ ether_type: EtherType::IPv4,
+ },
+ &bytes[14..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[1],
+ Encapsulation::Ipv4(
+ Ipv4Header {
+ version: 4,
+ ihl: 20,
+ tos: 0x00,
+ length: 148,
+ id: 0x0000,
+ flags: 0x2,
+ frag_offset: 0,
+ ttl: 255,
+ protocol: IPProtocol::GRE,
+ checksum: 0x0127,
+ source_address: Ipv4Addr::new(72, 205, 54, 70),
+ dest_address: Ipv4Addr::new(86, 106, 164, 150),
+ },
+ &bytes[34..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[2],
+ Encapsulation::Grev0(
+ Grev0Header {
+ flag_checksum: false,
+ flag_routing: false,
+ flag_key: false,
+ flag_sequence: false,
+ flag_strictroute: false,
+ recursion_control: 0,
+ flags: 0,
+ version: 0,
+ protocol_type: EtherType::IPv4,
+ checksum: None,
+ offset: None,
+ key: None,
+ sequence_number: None,
+ routing: None,
+ },
+ &bytes[38..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[3],
+ Encapsulation::Ipv4(
+ Ipv4Header {
+ version: 4,
+ ihl: 20,
+ tos: 0x00,
+ length: 124,
+ id: 0x27e0,
+ flags: 0x0,
+ frag_offset: 0,
+ ttl: 254,
+ protocol: IPProtocol::GRE,
+ checksum: 0x685b,
+ source_address: Ipv4Addr::new(10, 10, 11, 2),
+ dest_address: Ipv4Addr::new(10, 10, 13, 2),
+ },
+ &bytes[58..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[4],
+ Encapsulation::Grev0(
+ Grev0Header {
+ flag_checksum: false,
+ flag_routing: false,
+ flag_key: false,
+ flag_sequence: false,
+ flag_strictroute: false,
+ recursion_control: 0,
+ flags: 0,
+ version: 0,
+ protocol_type: EtherType::IPv4,
+ checksum: None,
+ offset: None,
+ key: None,
+ sequence_number: None,
+ routing: None,
+ },
+ &bytes[62..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[5],
+ Encapsulation::Ipv4(
+ Ipv4Header {
+ version: 4,
+ ihl: 20,
+ tos: 0x00,
+ length: 100,
+ id: 0x23b8,
+ flags: 0x0,
+ frag_offset: 0,
+ ttl: 255,
+ protocol: IPProtocol::ICMP,
+ checksum: 0xb32b,
+ source_address: Ipv4Addr::new(10, 10, 25, 1),
+ dest_address: Ipv4Addr::new(192, 168, 1, 2),
+ },
+ &bytes[82..]
+ )
+ );
+ assert_eq!(
+ packet.encapsulation[6],
+ Encapsulation::Icmp(
+ IcmpHeader {
+ icmp_type: IcmpType::EchoRequest,
+ icmp_code: 0,
+ icmp_checksum: 0xcbca,
+ icmp_extended: vec![0x00, 0x17, 0x1d, 0xf2],
+ },
+ &bytes[90..]
+ )
+ );
+
+ // assert_eq!(1, 0);
+ }
}
diff --git a/src/protocol/ethernet.rs b/src/protocol/ethernet.rs
index a588c0d..6c42c2d 100644
--- a/src/protocol/ethernet.rs
+++ b/src/protocol/ethernet.rs
@@ -25,6 +25,7 @@ pub enum EtherType {
IPX, // IPX [Xerox]
Qnet, // QNX Qnet [QNX Software Systems]
IPv6, // Internet Protocol Version 6 (IPv6) [RFC7042]
+ PPP, // Point-to-Point Protocol (PPP) [RFC7042]
FlowControl, // Ethernet Flow Control [IEEE 802.3x]
CobraNet, // CobraNet [CobraNet]
MPLSuni, // MPLS Unicast [RFC 3032]
@@ -88,6 +89,7 @@ impl From<u16> for EtherType {
0x8137 => Self::IPX,
0x8204 => Self::Qnet,
0x86DD => Self::IPv6,
+ 0x880b => Self::PPP,
0x8808 => Self::FlowControl,
0x8819 => Self::CobraNet,
0x8847 => Self::MPLSuni,
diff --git a/src/protocol/grev0.rs b/src/protocol/grev0.rs
new file mode 100644
index 0000000..fd368b9
--- /dev/null
+++ b/src/protocol/grev0.rs
@@ -0,0 +1,334 @@
+use crate::protocol::codec::Decode;
+use crate::protocol::ethernet::EtherType;
+use nom::bits;
+use nom::bytes;
+use nom::error::Error;
+use nom::number;
+use nom::sequence;
+use nom::IResult;
+
+/******************************************************************************
+ * Struct
+ ******************************************************************************/
+
+/*
+ * GRE Header Format (Version 0)
+ *
+ * 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Checksum (optional) | Offset (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing (optional)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Address Family | SRE Offset | SRE Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing Information ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * https://datatracker.ietf.org/doc/html/rfc1701
+ * https://datatracker.ietf.org/doc/html/rfc2890
+ */
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct SourceRouteEntry {
+ pub address_family: u16,
+ pub sre_offset: u8,
+ pub sre_length: u8,
+ pub sre_routing: Vec<u8>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Grev0Header {
+ pub flag_checksum: bool,
+ pub flag_routing: bool,
+ pub flag_key: bool,
+ pub flag_sequence: bool,
+ pub flag_strictroute: bool,
+ pub recursion_control: u8,
+ pub flags: u8,
+ pub version: u8,
+ pub protocol_type: EtherType,
+ pub checksum: Option<u16>,
+ pub offset: Option<u16>,
+ pub key: Option<u32>,
+ pub sequence_number: Option<u32>,
+ pub routing: Option<Vec<SourceRouteEntry>>,
+}
+
+/******************************************************************************
+ * API
+ ******************************************************************************/
+
+fn source_route_entry_decode(input: &[u8]) -> IResult<&[u8], SourceRouteEntry> {
+ let (input, address_family) = number::streaming::be_u16(input)?;
+ let (input, sre_offset) = number::streaming::be_u8(input)?;
+ let (input, sre_length) = number::streaming::be_u8(input)?;
+ /*
+ * The routing field is terminated with a "NULL" SRE containing an
+ * address family of type 0x0000 and a length of 0.
+ */
+ let (input, sre_routing) = match (address_family, sre_length) {
+ (_, 0) => (input, vec![]),
+ (_, _) => bytes::streaming::take(sre_length)(input).map(|(i, l)| (i, l.to_vec()))?,
+ };
+ Ok((
+ input,
+ SourceRouteEntry {
+ address_family,
+ sre_offset,
+ sre_length,
+ sre_routing,
+ },
+ ))
+}
+
+impl Decode for Grev0Header {
+ type Iterm = Grev0Header;
+ fn decode(input: &[u8]) -> IResult<&[u8], Grev0Header> {
+ let (
+ input,
+ (
+ flag_checksum,
+ flag_routing,
+ flag_key,
+ flag_sequence,
+ flag_strictroute,
+ recursion_control,
+ flags,
+ version,
+ ),
+ ): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8)) =
+ bits::bits::<_, _, Error<_>, _, _>(sequence::tuple((
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(3u8),
+ bits::streaming::take(5u8),
+ bits::streaming::take(3u8),
+ )))(input)?;
+ if version != 0 {
+ return Err(nom::Err::Error(Error::new(
+ input,
+ nom::error::ErrorKind::Verify,
+ )));
+ }
+
+ let (input, protocol_type) = EtherType::decode(input)?;
+ let (input, checksum) = match (flag_checksum, flag_routing) {
+ (0, 0) => (input, None),
+ (_, _) => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?,
+ };
+ let (input, offset) = match (flag_checksum, flag_routing) {
+ (0, 0) => (input, None),
+ (_, _) => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?,
+ };
+ let (input, key) = match flag_key {
+ 0 => (input, None),
+ _ => number::streaming::be_u32(input).map(|(i, l)| (i, Some(l)))?,
+ };
+ let (input, sequence_number) = match flag_sequence {
+ 0 => (input, None),
+ _ => number::streaming::be_u32(input).map(|(i, l)| (i, Some(l)))?,
+ };
+ let (input, routing) = match flag_routing {
+ 0 => (input, None),
+ _ => {
+ let mut left = input;
+ let mut routing = Vec::new();
+ loop {
+ let (i, sre) = source_route_entry_decode(left)?;
+ let length = sre.sre_length;
+ routing.push(sre);
+ left = i;
+ if length == 0 {
+ break;
+ }
+ }
+ (left, Some(routing))
+ }
+ };
+
+ Ok((
+ input,
+ Grev0Header {
+ flag_checksum: flag_checksum == 1,
+ flag_routing: flag_routing == 1,
+ flag_key: flag_key == 1,
+ flag_sequence: flag_sequence == 1,
+ flag_strictroute: flag_strictroute == 1,
+ recursion_control,
+ flags,
+ version,
+ protocol_type,
+ checksum,
+ offset,
+ key,
+ sequence_number,
+ routing,
+ },
+ ))
+ }
+}
+
+/******************************************************************************
+ * TEST
+ ******************************************************************************/
+
+#[cfg(test)]
+mod tests {
+ use super::Grev0Header;
+ use super::SourceRouteEntry;
+ use crate::protocol::codec::Decode;
+ use crate::protocol::ethernet::EtherType;
+ const LAST_SLICE: &'static [u8] = &[0xff];
+
+ #[test]
+ // Enable Key Bit
+ fn grev0_header_decode1() {
+ /*
+ * Generic Routing Encapsulation (IP)
+ * Flags and Version: 0x2000
+ * 0... .... .... .... = Checksum Bit: No
+ * .0.. .... .... .... = Routing Bit: No
+ * ..1. .... .... .... = Key Bit: Yes
+ * ...0 .... .... .... = Sequence Number Bit: No
+ * .... 0... .... .... = Strict Source Route Bit: No
+ * .... .000 .... .... = Recursion control: 0
+ * .... .... 0000 0... = Flags (Reserved): 0
+ * .... .... .... .000 = Version: GRE (0)
+ * Protocol Type: IP (0x0800)
+ * Key: 0x00000384
+ */
+
+ let bytes = [
+ 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x84, 0xff, /* Payload */
+ ];
+
+ let expectation = Grev0Header {
+ flag_checksum: false,
+ flag_routing: false,
+ flag_key: true,
+ flag_sequence: false,
+ flag_strictroute: false,
+ recursion_control: 0,
+ flags: 0,
+ version: 0,
+ protocol_type: EtherType::IPv4,
+ checksum: None,
+ offset: None,
+ key: Some(0x384),
+ sequence_number: None,
+ routing: None,
+ };
+
+ assert_eq!(Grev0Header::decode(&bytes), Ok((LAST_SLICE, expectation)));
+
+ // example
+ let result = Grev0Header::decode(&bytes);
+ if let Ok((payload, header)) = result {
+ println!("return: {:?}, payload: {}", header, payload.len());
+ } else {
+ println!("return: Incomplete data");
+ }
+ }
+
+ #[test]
+ // Enable Routing Bit
+ fn grev0_header_decode2() {
+ /*
+ * Generic Routing Encapsulation (IP)
+ * Flags and Version: 0xc000
+ * 1... .... .... .... = Checksum Bit: Yes
+ * .1.. .... .... .... = Routing Bit: Yes
+ * ..0. .... .... .... = Key Bit: No
+ * ...0 .... .... .... = Sequence Number Bit: No
+ * .... 0... .... .... = Strict Source Route Bit: No
+ * .... .000 .... .... = Recursion control: 0
+ * .... .... 0000 0... = Flags (Reserved): 0
+ * .... .... .... .000 = Version: GRE (0)
+ * Protocol Type: IP (0x0800)
+ * Checksum: 0x0000 incorrect, should be 0xea95
+ * [Expert Info (Warning/Protocol): Incorrect GRE Checksum [should be 0xea95]]
+ * [Incorrect GRE Checksum [should be 0xea95]]
+ * [Severity level: Warning]
+ * [Group: Protocol]
+ * [Checksum Status: Bad]
+ * Offset: 44
+ * Routing
+ * Address Family: 2
+ * SRE Offset: 0
+ * SRE Length: 44
+ * Routing Information: 6c696e6b5f696e666f206c696e6b5f696e666f206c696e6b5f696e666f206c696e6b5f69…
+ * Routing
+ * Address Family: 0
+ * SRE Offset: 0
+ * SRE Length: 0
+ */
+
+ let bytes = [
+ 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x2c, 0x6c, 0x69,
+ 0x6e, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69,
+ 0x6e, 0x66, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20,
+ 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, /* Payload */
+ ];
+
+ let expectation = Grev0Header {
+ flag_checksum: true,
+ flag_routing: true,
+ flag_key: false,
+ flag_sequence: false,
+ flag_strictroute: false,
+ recursion_control: 0,
+ flags: 0,
+ version: 0,
+ protocol_type: EtherType::IPv4,
+ checksum: Some(0x0000),
+ offset: Some(44),
+ key: None,
+ sequence_number: None,
+ routing: Some(vec![
+ SourceRouteEntry {
+ address_family: 2,
+ sre_offset: 0,
+ sre_length: 44,
+ sre_routing: vec![
+ 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x6c, 0x69,
+ 0x6e, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x6b,
+ 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69,
+ 0x6e, 0x66, 0x6f, 0x20, 0x00, 0x00, 0x00, 0x00,
+ ],
+ },
+ SourceRouteEntry {
+ address_family: 0,
+ sre_offset: 0,
+ sre_length: 0,
+ sre_routing: vec![],
+ },
+ ]),
+ };
+
+ assert_eq!(Grev0Header::decode(&bytes), Ok((LAST_SLICE, expectation)));
+
+ // example
+ let result = Grev0Header::decode(&bytes);
+ if let Ok((payload, header)) = result {
+ println!("return: {:?}, payload: {}", header, payload.len());
+ } else {
+ println!("return: Incomplete data");
+ }
+ }
+}
diff --git a/src/protocol/grev1.rs b/src/protocol/grev1.rs
new file mode 100644
index 0000000..4bf551f
--- /dev/null
+++ b/src/protocol/grev1.rs
@@ -0,0 +1,186 @@
+use crate::protocol::codec::Decode;
+use crate::protocol::ethernet::EtherType;
+use nom::bits;
+use nom::error::Error;
+use nom::number;
+use nom::sequence;
+use nom::IResult;
+
+/******************************************************************************
+ * Struct
+ ******************************************************************************/
+
+/*
+ * Enhanced GRE header (Version 1)
+ *
+ * 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (HW) Payload Length | Key (LW) Call ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Acknowledgment Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * https://datatracker.ietf.org/doc/html/rfc2637
+ */
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Grev1Header {
+ pub flag_checksum: bool,
+ pub flag_routing: bool,
+ pub flag_key: bool,
+ pub flag_sequence: bool,
+ pub flag_strictroute: bool,
+ pub recursion_control: u8,
+ pub flag_acknowledgment: bool,
+ pub flags: u8,
+ pub version: u8,
+ pub protocol_type: EtherType,
+ pub key_payload_length: u16,
+ pub key_call_id: u16,
+ pub sequence_number: Option<u32>,
+ pub acknowledgment_number: Option<u32>,
+}
+
+/******************************************************************************
+ * API
+ ******************************************************************************/
+
+impl Decode for Grev1Header {
+ type Iterm = Grev1Header;
+ fn decode(input: &[u8]) -> IResult<&[u8], Grev1Header> {
+ let (
+ input,
+ (
+ flag_checksum,
+ flag_routing,
+ flag_key,
+ flag_sequence,
+ flag_strictroute,
+ recursion_control,
+ flag_acknowledgment,
+ flags,
+ version,
+ ),
+ ): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8, u8)) =
+ bits::bits::<_, _, Error<_>, _, _>(sequence::tuple((
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(3u8),
+ bits::streaming::take(1u8),
+ bits::streaming::take(4u8),
+ bits::streaming::take(3u8),
+ )))(input)?;
+ if version != 1 {
+ return Err(nom::Err::Error(Error::new(
+ input,
+ nom::error::ErrorKind::Verify,
+ )));
+ }
+
+ let (input, protocol_type) = EtherType::decode(input)?;
+ let (input, key_payload_length) = number::streaming::be_u16(input)?;
+ let (input, key_call_id) = number::streaming::be_u16(input)?;
+ let (input, sequence_number) = match flag_sequence {
+ 0 => (input, None),
+ _ => number::streaming::be_u32(input).map(|(i, l)| (i, Some(l)))?,
+ };
+ let (input, acknowledgment_number) = match flag_acknowledgment {
+ 0 => (input, None),
+ _ => number::streaming::be_u32(input).map(|(i, l)| (i, Some(l)))?,
+ };
+
+ Ok((
+ input,
+ Grev1Header {
+ flag_checksum: flag_checksum == 1,
+ flag_routing: flag_routing == 1,
+ flag_key: flag_key == 1,
+ flag_sequence: flag_sequence == 1,
+ flag_strictroute: flag_strictroute == 1,
+ recursion_control,
+ flag_acknowledgment: flag_acknowledgment == 1,
+ flags,
+ version,
+ protocol_type,
+ key_payload_length,
+ key_call_id,
+ sequence_number,
+ acknowledgment_number,
+ },
+ ))
+ }
+}
+
+/******************************************************************************
+ * TEST
+ ******************************************************************************/
+
+#[cfg(test)]
+mod tests {
+ use super::Grev1Header;
+ use crate::protocol::codec::Decode;
+ use crate::protocol::ethernet::EtherType;
+ const LAST_SLICE: &'static [u8] = &[0xff];
+
+ #[test]
+ fn grev1_header_decode() {
+ /*
+ * Generic Routing Encapsulation (PPP)
+ * Flags and Version: 0x3081
+ * 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
+ * .... .... 1... .... = Acknowledgment: Yes
+ * .... .... .000 0... = Flags (Reserved): 0
+ * .... .... .... .001 = Version: Enhanced GRE (1)
+ * Protocol Type: PPP (0x880b)
+ * Payload Length: 103
+ * Call ID: 6016
+ * Sequence Number: 430001
+ * Acknowledgment Number: 539254
+ */
+
+ let bytes = [
+ 0x30, 0x81, 0x88, 0x0b, 0x00, 0x67, 0x17, 0x80, 0x00, 0x06, 0x8f, 0xb1, 0x00, 0x08,
+ 0x3a, 0x76, 0xff, /* Payload */
+ ];
+
+ let expectation = Grev1Header {
+ flag_checksum: false,
+ flag_routing: false,
+ flag_key: true,
+ flag_sequence: true,
+ flag_strictroute: false,
+ recursion_control: 0,
+ flag_acknowledgment: true,
+ flags: 0,
+ version: 1,
+ protocol_type: EtherType::PPP,
+ key_payload_length: 103,
+ key_call_id: 6016,
+ sequence_number: Some(430001),
+ acknowledgment_number: Some(539254),
+ };
+
+ assert_eq!(Grev1Header::decode(&bytes), Ok((LAST_SLICE, expectation)));
+
+ // example
+ let result = Grev1Header::decode(&bytes);
+ if let Ok((payload, header)) = result {
+ println!("return: {:?}, payload: {}", header, payload.len());
+ } else {
+ println!("return: Incomplete data");
+ }
+ }
+}
diff --git a/src/protocol/ip.rs b/src/protocol/ip.rs
index c9b4f53..1ff15e3 100644
--- a/src/protocol/ip.rs
+++ b/src/protocol/ip.rs
@@ -29,6 +29,7 @@ pub enum IPProtocol {
IPV6,
IPV6ROUTING,
IPV6FRAGMENT,
+ GRE,
ESP,
AUTH,
ICMP6,
@@ -64,6 +65,7 @@ impl From<u8> for IPProtocol {
41 => IPProtocol::IPV6,
43 => IPProtocol::IPV6ROUTING, // IPv6 Routing Header
44 => IPProtocol::IPV6FRAGMENT, // IPv6 Fragment Header
+ 47 => IPProtocol::GRE, // GRE encapsulation [RFC2784][RFC2890]
50 => IPProtocol::ESP, // Encap Security Payload [RFC4303]
51 => IPProtocol::AUTH, // Authentication Header [RFC4302]
58 => IPProtocol::ICMP6,
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 7c8ade2..4f82280 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -12,4 +12,6 @@ pub mod icmp;
pub mod icmpv6;
pub mod mpls;
pub mod gtpv1;
-pub mod l2tp; \ No newline at end of file
+pub mod l2tp;
+pub mod grev0;
+pub mod grev1; \ No newline at end of file