summaryrefslogtreecommitdiff
path: root/src/protocol/grev0.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol/grev0.rs')
-rw-r--r--src/protocol/grev0.rs334
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");
+ }
+ }
+}