summaryrefslogtreecommitdiff
path: root/src/protocol
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 /src/protocol
parentcb674f9e168b6e709136e17a5bc87d3925c6f479 (diff)
[feature] Support GREv0/GREv1 Decode
Diffstat (limited to 'src/protocol')
-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
5 files changed, 527 insertions, 1 deletions
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