use sawp::error::{Error, ErrorKind, Result};
use sawp::parser::{Direction, Parse};
use sawp::probe::{Probe, Status};
use sawp::protocol::Protocol;
use sawp_flags::BitFlags;
pub use sawp_flags::{Flag, Flags};
use nom::bytes::streaming::take;
use nom::number::streaming::{be_u16, be_u32, be_u8};
use std::ops::BitAnd;
const MAX_SRE_ENTRIES: u32 = 10;
const ETHERTYPE_PPP: u16 = 0x880b;
#[allow(non_camel_case_types)]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, BitFlags)]
pub enum ErrorFlags {
RESERVE = 0b0000_0001,
VERSION = 0b0000_0010,
RESERVE1 = 0b0000_0100,
MAX_SRE_REACHED = 0b0000_1000,
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq, BitFlags)]
#[repr(u16)]
pub enum GreFlags {
CHECKSUM = 0b1000_0000_0000_0000,
ROUTING = 0b0100_0000_0000_0000,
KEY = 0b0010_0000_0000_0000,
SEQUENCE_NUMBER = 0b0001_0000_0000_0000,
STRICT_SOURCE_ROUTE = 0b0000_1000_0000_0000,
RECURSION = 0b0000_0111_0000_0000,
ACKNOWLEDGEMENT = 0b0000_0000_1000_0000,
FLAGS = 0b0000_0000_0111_1000,
VERSION = 0b0000_0000_0000_0111,
RESERVED_GRE = 0b0111_1111_1111_1111,
RESERVED_DEPRECATED_GRE = 0b0000_0111_1111_1111,
RESERVED_PPTP = 0b1100_1111_0111_1110,
RESERVED_FLAGS = 0b0000_0111_0111_1000,
VERSION_GRE = 0b0000_0000_0000_0000,
VERSION_PPTP = 0b0000_0000_0000_0001,
}
#[derive(Debug, PartialEq)]
pub struct SourceRouteEntry {
address_family: u16,
sre_offset: u8,
sre_length: u8,
routing_info: Vec<u8>,
}
#[derive(Debug, PartialEq)]
pub enum Data {
Gre {
checksum: Option<u16>,
reserved: Option<u16>,
},
GreDeprecated {
checksum: Option<u16>,
offset: Option<u16>,
key: Option<u32>,
sequence_number: Option<u32>,
source_route_entries: Vec<SourceRouteEntry>,
},
Pptp {
payload_length: u16,
call_id: u16,
sequence_number: Option<u32>,
acknowledgement_number: Option<u32>,
payload: Vec<u8>,
},
Empty,
}
#[derive(Debug)]
pub struct Gre {}
#[derive(Debug, PartialEq)]
pub struct Message {
pub header: Flags<GreFlags>,
pub protocol_type: u16,
pub data: Data,
pub error_flags: Flags<ErrorFlags>,
}
impl Message {
fn is_checksum_set(&self) -> bool {
self.header.intersects(GreFlags::CHECKSUM)
}
fn is_routing_set(&self) -> bool {
self.header.intersects(GreFlags::ROUTING)
}
fn is_key_set(&self) -> bool {
self.header.intersects(GreFlags::KEY)
}
fn is_sequence_number_set(&self) -> bool {
self.header.intersects(GreFlags::SEQUENCE_NUMBER)
}
fn is_acknowledgement_set(&self) -> bool {
self.header.intersects(GreFlags::ACKNOWLEDGEMENT)
}
fn version(&self) -> Flags<GreFlags> {
self.header.bitand(GreFlags::VERSION)
}
fn is_reserved_gre_set(&self) -> bool {
self.header.intersects(GreFlags::RESERVED_GRE)
}
fn is_reserved_deprecated_gre_set(&self) -> bool {
self.header.intersects(GreFlags::RESERVED_DEPRECATED_GRE)
}
fn is_reserved_pptp_set(&self) -> bool {
self.header.intersects(GreFlags::RESERVED_PPTP)
}
fn is_reserved_flags_set(&self) -> bool {
self.header.intersects(GreFlags::RESERVED_FLAGS)
}
fn is_valid_gre(&self) -> bool {
!self.is_reserved_gre_set()
}
fn is_valid_deprecated_gre(&self) -> bool {
!self.is_reserved_deprecated_gre_set()
}
fn is_valid_pptp(&self) -> bool {
!self.is_reserved_pptp_set()
&& self.is_key_set()
&& self.version().contains(GreFlags::VERSION_PPTP)
&& self.protocol_type == ETHERTYPE_PPP
}
fn parse_checksum_and_routing<'a>(
&mut self,
input: &'a [u8],
) -> Result<(&'a [u8], Option<u16>, Option<u16>)> {
if self.is_checksum_set() || self.is_routing_set() {
let (input, checksum) = be_u16(input)?;
let (input, offset) = be_u16(input)?;
Ok((input, Some(checksum), Some(offset)))
} else {
Ok((input, None, None))
}
}
fn parse_key<'a>(&mut self, input: &'a [u8]) -> Result<(&'a [u8], Option<u32>)> {
if self.is_key_set() {
let (input, key) = be_u32(input)?;
Ok((input, Some(key)))
} else {
Ok((input, None))
}
}
fn parse_sequence<'a>(&mut self, input: &'a [u8]) -> Result<(&'a [u8], Option<u32>)> {
if self.is_sequence_number_set() {
let (input, sequence) = be_u32(input)?;
Ok((input, Some(sequence)))
} else {
Ok((input, None))
}
}
fn parse_acknowledgement<'a>(&mut self, input: &'a [u8]) -> Result<(&'a [u8], Option<u32>)> {
if self.is_acknowledgement_set() {
let (input, acknowledgement) = be_u32(input)?;
Ok((input, Some(acknowledgement)))
} else {
Ok((input, None))
}
}
fn parse_source_route_entries<'a>(
&mut self,
input: &'a [u8],
) -> Result<(&'a [u8], Vec<SourceRouteEntry>)> {
if self.is_routing_set() {
let mut source_route_entries: Vec<SourceRouteEntry> = Vec::new();
let mut input_copy = input;
for _ in 0..MAX_SRE_ENTRIES {
let (input, address_family) = be_u16(input_copy)?;
let (input, sre_offset) = be_u8(input)?;
let (input, sre_length) = be_u8(input)?;
let (input, routing_raw) = take(sre_length)(input)?;
let source_route_entry = SourceRouteEntry {
address_family,
sre_offset,
sre_length,
routing_info: routing_raw.to_vec(),
};
source_route_entries.push(source_route_entry);
if address_family == 0 && sre_length == 0 {
return Ok((input, source_route_entries));
}
input_copy = input;
}
self.error_flags |= ErrorFlags::MAX_SRE_REACHED;
Ok((input_copy, source_route_entries))
} else {
Ok((input, vec![]))
}
}
fn parse_pptp_payload<'a>(
&mut self,
input: &'a [u8],
length: u16,
) -> Result<(&'a [u8], Vec<u8>)> {
if self.is_sequence_number_set() {
let (input, payload) = take(length)(input)?;
Ok((input, payload.to_vec()))
} else {
Ok((input, vec![]))
}
}
fn parse_gre<'a>(&mut self, input: &'a [u8]) -> Result<&'a [u8]> {
let (input, checksum, reserved) = self.parse_checksum_and_routing(input)?;
self.data = Data::Gre { checksum, reserved };
if let Some(reserved) = reserved {
if reserved > 0 {
self.error_flags |= ErrorFlags::RESERVE1;
}
}
Ok(input)
}
fn parse_deprecated<'a>(&mut self, input: &'a [u8]) -> Result<&'a [u8]> {
let (input, checksum, offset) = self.parse_checksum_and_routing(input)?;
let (input, key) = self.parse_key(input)?;
let (input, sequence_number) = self.parse_sequence(input)?;
let (input, source_route_entries) = self.parse_source_route_entries(input)?;
self.data = Data::GreDeprecated {
checksum,
offset,
key,
sequence_number,
source_route_entries,
};
Ok(input)
}
fn parse_pptp<'a>(&mut self, input: &'a [u8]) -> Result<&'a [u8]> {
let (input, payload_length) = be_u16(input)?;
let (input, call_id) = be_u16(input)?;
let (input, sequence_number) = self.parse_sequence(input)?;
let (input, acknowledgement_number) = self.parse_acknowledgement(input)?;
let (input, payload) = self.parse_pptp_payload(input, payload_length)?;
self.data = Data::Pptp {
payload_length,
call_id,
sequence_number,
acknowledgement_number,
payload,
};
Ok(input)
}
fn check_error_gre_flags(&mut self) {
if self.is_reserved_flags_set() {
self.error_flags |= ErrorFlags::RESERVE;
}
}
fn check_error_version_flags(&mut self) {
if self.version() != GreFlags::VERSION_PPTP && self.version() != GreFlags::VERSION_GRE {
self.error_flags |= ErrorFlags::VERSION;
}
}
}
impl Protocol<'_> for Gre {
type Message = Message;
fn name() -> &'static str {
"gre"
}
}
impl<'a> Probe<'a> for Gre {
fn probe(&self, input: &'a [u8], direction: Direction) -> Status {
match self.parse(input, direction) {
Ok((_, Some(msg))) => {
if msg.error_flags == ErrorFlags::none() {
Status::Recognized
} else {
Status::Unrecognized
}
}
Ok((_, _)) => Status::Recognized,
Err(Error {
kind: ErrorKind::Incomplete(_),
}) => Status::Incomplete,
Err(_) => Status::Unrecognized,
}
}
}
impl<'a> Parse<'a> for Gre {
fn parse(
&self,
input: &'a [u8],
_direction: Direction,
) -> Result<(&'a [u8], Option<Self::Message>)> {
let (input, gre_flags_raw) = be_u16(input)?;
let (input, protocol_type) = be_u16(input)?;
let mut message = Message {
header: Flags::from_bits(gre_flags_raw),
protocol_type,
data: Data::Empty,
error_flags: ErrorFlags::none(),
};
if message.is_valid_gre() {
let input = message.parse_gre(input)?;
Ok((input, Some(message)))
} else if message.is_valid_deprecated_gre() {
let input = message.parse_deprecated(input)?;
Ok((input, Some(message)))
} else if message.is_valid_pptp() {
let input = message.parse_pptp(input)?;
Ok((input, Some(message)))
} else {
message.check_error_gre_flags();
message.check_error_version_flags();
Ok((input, Some(message)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use sawp::probe::Status;
#[test]
fn test_name() {
assert_eq!(Gre::name(), "gre");
}
#[rstest(
input,
expected,
case::empty(b"", Err(Error::incomplete_needed(2))),
case::basic_ip(
&[
// header: No flags set. Version zero.
0x00, 0x00,
// protocol type (ip)
0x08, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::none(),
protocol_type: 0x0800,
data: Data::Gre {
checksum: None,
reserved: None,
},
error_flags: ErrorFlags::none(),
})))),
case::checksum(
&[
// header: Checksum flag set. Version zero.
0x80, 0x00,
// protocol type IPV6
0x86, 0xdd,
// checksum bytes
0xab, 0xcd,
// reserved1: zero
0x00, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::CHECKSUM.into(),
protocol_type: 0x86dd,
data: Data::Gre {
checksum: Some(43981),
reserved: Some(0),
},
error_flags: ErrorFlags::none(),
})))),
case::checksum_missing(
&[
// header: Checksum flag set. Version zero.
0x80, 0x00,
// protocol type IPV6
0x86, 0xdd,
],
Err(Error::incomplete_needed(2))),
case::error_reserve(
&[
// header: No checksum and non zero reserves. Version zero.
0x7a, 0x00,
// protocol type (ip)
0x08, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: Flags::from_bits(31232),
protocol_type: 0x0800,
data: Data::Empty,
error_flags: ErrorFlags::RESERVE.into(),
})))),
case::error_version(
&[
0x00, 0x02,
0x88, 0xbe,
],
Ok((&[] as &[u8], Some(Message{
header: Flags::from_bits(2),
protocol_type: 0x88be,
data: Data::Empty,
error_flags: ErrorFlags::VERSION.into(),
})))),
case::error_reserve1(
&[
0x80, 0x00,
0x86, 0xdd,
0xab, 0xcd,
0x00, 0x40,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::CHECKSUM.into(),
protocol_type: 0x86dd,
data: Data::Gre {
checksum: Some(43981),
reserved: Some(64),
},
error_flags: ErrorFlags::RESERVE1.into(),
})))),
case::deprecated_routing_no_sre(
&[
0x40, 0x00,
0x08, 0x00,
0x00, 0x43,
0x00, 0x21,
0x00, 0x00, 0x00, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::ROUTING.into(),
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: Some(67),
offset: Some(33),
key: None,
sequence_number: None,
source_route_entries: vec![SourceRouteEntry {
address_family: 0,
sre_offset: 0,
sre_length: 0,
routing_info: vec![],
}],
},
error_flags: ErrorFlags::none(),
})))),
case::deprecated_routing_sre(
&[
0xc0, 0x00,
0x08, 0x00,
0x00, 0x43,
0x00, 0x21,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0xab, 0xcd, 0xef, 0x08,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::CHECKSUM | GreFlags::ROUTING,
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: Some(67),
offset: Some(33),
key: None,
sequence_number: None,
source_route_entries: vec![SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 43981,
sre_offset: 239,
sre_length: 8,
routing_info: vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 0,
sre_offset: 0,
sre_length: 0,
routing_info: vec![],
}],
},
error_flags: ErrorFlags::none(),
})))),
case::deprecated_routing_max_sre(
&[
0xc0, 0x00,
0x08, 0x00,
0x00, 0x43,
0x00, 0x21,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::CHECKSUM | GreFlags::ROUTING,
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: Some(67),
offset: Some(33),
key: None,
sequence_number: None,
source_route_entries: vec![SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}, SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xff, 0xff, 0xff, 0xff],
}],
},
error_flags: ErrorFlags::MAX_SRE_REACHED.into(),
})))),
case::deprecated_routing_sre_missing(
&[
0xc0, 0x00,
0x08, 0x00,
0x00, 0x43,
0x00, 0x21,
0x12, 0x34, 0x56, 0x04,
0xff, 0xff, 0xff, 0xff,
0xab, 0xcd, 0xef, 0x08,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
],
Err(Error::incomplete_needed(2))),
case::deprecated_key(
&[
0x20,
0x00,
0x08, 0x00,
0x12, 0x34, 0x56, 0x78,
],
Ok((&[] as &[u8], Some(Message {
header: GreFlags::KEY.into(),
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: None,
offset: None,
key: Some(305_419_896),
sequence_number: None,
source_route_entries: vec![],
},
error_flags: ErrorFlags::none(),
})))),
case::deprecated_key_missing(
&[
0x20, 0x00,
0x08, 0x00,
0x12, 0x34, 0x56,
],
Err(Error::incomplete_needed(1))),
case::deprecated_sequence(
&[
0x10, 0x00,
0x08, 0x00,
0xfe, 0xdc, 0xba, 0x98,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::SEQUENCE_NUMBER.into(),
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: None,
offset: None,
key: None,
sequence_number: Some(4_275_878_552),
source_route_entries: vec![],
},
error_flags: ErrorFlags::none(),
})))),
case::deprecated_sequence_missing(
&[
0x10, 0x00,
0x08, 0x00,
0xfe, 0xdc,
],
Err(Error::incomplete_needed(2))),
case::deprecated_all(
&[
0xf8, 0x00,
0x08, 0x00,
0x12, 0x34,
0x56, 0x78,
0x9a, 0xbc, 0xde, 0xf1,
0x23, 0x45, 0x67, 0x89,
0x12, 0x34, 0x56, 0x04,
0xab, 0xcd, 0xef, 0x12,
0xab, 0xcd, 0xef, 0x08,
0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xf1,
0x00, 0x00, 0x00, 0x00,
],
Ok((&[] as &[u8], Some(Message{
header: Flags::from_bits(63488),
protocol_type: 0x0800,
data: Data::GreDeprecated {
checksum: Some(4660),
offset: Some(22136),
key: Some(2_596_069_105),
sequence_number: Some(591_751_049),
source_route_entries: vec![SourceRouteEntry {
address_family: 4660,
sre_offset: 86,
sre_length: 4,
routing_info: vec![0xab, 0xcd, 0xef, 0x12],
}, SourceRouteEntry {
address_family: 43981,
sre_offset: 239,
sre_length: 8,
routing_info: vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1],
}, SourceRouteEntry {
address_family: 0,
sre_offset: 0,
sre_length: 0,
routing_info: vec![],
}],
},
error_flags: ErrorFlags::none(),
})))),
case::deprecated_error_recur(
&[
0xc2, 0x00,
0x08, 0x00,
],
Ok((&[] as &[u8], Some(Message {
header: Flags::from_bits(49664),
protocol_type: 0x0800,
data: Data::Empty,
error_flags: ErrorFlags::RESERVE.into(),
})))),
case::deprecated_error_version(
&[
0xe0, 0x02,
0x08, 0x00,
],
Ok((&[] as &[u8], Some(Message {
header: Flags::from_bits(57346),
protocol_type: 0x0800,
data: Data::Empty,
error_flags: ErrorFlags::VERSION.into(),
})))),
case::pptp(
&[
0x20, 0x01,
0x88, 0x0b,
0x00, 0x00, 0x00, 0x2f,
],
Ok((&[] as &[u8], Some(Message {
header: Flags::from_bits(8193),
protocol_type: 0x880b,
data: Data::Pptp {
payload_length: 0,
call_id: 47,
sequence_number: None,
acknowledgement_number: None,
payload: vec![],
},
error_flags: ErrorFlags::none(),
})))),
case::pptp_missing(
&[
0x20, 0x01,
0x88, 0x0b,
0x00, 0x00,
],
Err(Error::incomplete_needed(2))),
case::pptp_sequence(
&[
0x30, 0x01,
0x88, 0x0b,
0x00, 0x08, 0x00, 0x2f,
0x00, 0x00, 0x00, 0x01,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::SEQUENCE_NUMBER | GreFlags::KEY | GreFlags::VERSION_PPTP,
protocol_type: 0x880b,
data: Data::Pptp {
payload_length: 8,
call_id: 47,
sequence_number: Some(1),
acknowledgement_number: None,
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1],
},
error_flags: ErrorFlags::none(),
})))),
case::pptp_sequence_missing_payload(
&[
0x30, 0x01,
0x88, 0x0b,
0x00, 0x08, 0x00, 0x2f,
0x00, 0x00, 0x00, 0x01,
],
Err(Error::incomplete_needed(8))),
case::pptp_acknowledgement(
&[
0x20, 0x81,
0x88, 0x0b,
0x00, 0x00, 0x00, 0x2f,
0x12, 0x34, 0x56, 0x78,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::KEY | GreFlags::ACKNOWLEDGEMENT | GreFlags::VERSION_PPTP,
protocol_type: 0x880b,
data: Data::Pptp {
payload_length: 0,
call_id: 47,
sequence_number: None,
acknowledgement_number: Some(305_419_896),
payload: vec![],
},
error_flags: ErrorFlags::none(),
})))),
case::pptp_acknowledgement_missing(
&[
0x20, 0x81,
0x88, 0x0b,
0x00, 0x00, 0x00, 0x2f,
0x12, 0x34, 0x78,
],
Err(Error::incomplete_needed(1))),
case::pptp_all(
&[
0x30, 0x81,
0x88, 0x0b,
0x00, 0x04, 0x00, 0xff,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c,
],
Ok((&[] as &[u8], Some(Message{
header: GreFlags::KEY | GreFlags::SEQUENCE_NUMBER | GreFlags::ACKNOWLEDGEMENT | GreFlags::VERSION_PPTP,
protocol_type: 0x880b,
data: Data::Pptp {
payload_length: 4,
call_id: 255,
sequence_number: Some(16_909_060),
acknowledgement_number: Some(84_281_096),
payload: vec![0x09, 0x0a, 0x0b, 0x0c],
},
error_flags: ErrorFlags::none(),
})))),
)]
fn test_parse(input: &[u8], expected: Result<(&[u8], Option<Message>)>) {
let gre = Gre {};
assert_eq!(gre.parse(input, Direction::Unknown), expected);
}
#[rstest(
input,
expected,
case::empty(b"", Status::Incomplete),
case::hello_world(b"hello world", Status::Unrecognized),
case::basic_valid(
&[
// gre_flags: no checksum
0x00,
// version: must be zero
0x00,
// protocol type (ip)
0x08, 0x00,
],
Status::Recognized
),
case::basic_invalid(
&[
// gre_flags: no checksum
0x00,
// version: non-zero (error)
0x02,
// protocol type (ip)
0x88, 0xbe,
// checksum: garbage
0xab, 0xcd,
0xff, 0xff,
],
Status::Unrecognized
)
)]
fn test_probe(input: &[u8], expected: Status) {
let gre = Gre {};
assert_eq!(gre.probe(input, Direction::Unknown), expected)
}
}