diff options
Diffstat (limited to 'src/protocol')
| -rw-r--r-- | src/protocol/ethernet.rs | 2 | ||||
| -rw-r--r-- | src/protocol/grev0.rs | 334 | ||||
| -rw-r--r-- | src/protocol/grev1.rs | 186 | ||||
| -rw-r--r-- | src/protocol/ip.rs | 2 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 4 |
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 |
