From ae80f71eba09d4381bb070d87bcd2aa6173166c6 Mon Sep 17 00:00:00 2001 From: luwenpeng Date: Tue, 19 Sep 2023 15:21:20 +0800 Subject: [feature] Support GREv0/GREv1 Decode --- src/main.rs | 6 + src/packet/error.rs | 11 ++ src/packet/packet.rs | 328 ++++++++++++++++++++++++++++++++++++++++++++++ src/protocol/ethernet.rs | 2 + src/protocol/grev0.rs | 334 +++++++++++++++++++++++++++++++++++++++++++++++ src/protocol/grev1.rs | 186 ++++++++++++++++++++++++++ src/protocol/ip.rs | 2 + src/protocol/mod.rs | 4 +- 8 files changed, 872 insertions(+), 1 deletion(-) create mode 100644 src/protocol/grev0.rs create mode 100644 src/protocol/grev1.rs 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 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, +} + +#[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, + pub offset: Option, + pub key: Option, + pub sequence_number: Option, + pub routing: Option>, +} + +/****************************************************************************** + * 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, + pub acknowledgment_number: Option, +} + +/****************************************************************************** + * 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 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 -- cgit v1.2.3