diff options
Diffstat (limited to 'src/protocol/grev0.rs')
| -rw-r--r-- | src/protocol/grev0.rs | 334 |
1 files changed, 334 insertions, 0 deletions
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"); + } + } +} |
