From f54739fbc0f01835e3dbc038b0b67f4b67859516 Mon Sep 17 00:00:00 2001 From: Mohammad Aadil Shabier Date: Tue, 5 Mar 2024 19:52:23 +0530 Subject: [PATCH] Implement IPv4 options: EOOL, NOP and RR - All other options are handled by a default IPOption::Other - IPv4 and IPv6 now implement PartialEq - Error reading Ipv4::frag_offset was fixed Signed-off-by: Mohammad Aadil Shabier --- src/layers/ipv4.rs | 170 ++++++++++++++++++++++++++++++++++++++++++-- src/layers/mod.rs | 2 +- src/types/ipaddr.rs | 4 +- 3 files changed, 166 insertions(+), 10 deletions(-) diff --git a/src/layers/ipv4.rs b/src/layers/ipv4.rs index 67fa4c6..10910e2 100644 --- a/src/layers/ipv4.rs +++ b/src/layers/ipv4.rs @@ -50,21 +50,44 @@ pub fn register_protocol(proto: u8, creator: LayerCreatorFn) -> Result<(), Error Ok(()) } +// Option Structs +#[derive(Debug, Serialize, PartialEq)] +struct IPOthers { + #[serde(rename = "type")] + type_: u8, + len: u8, + data: Vec, +} + +#[derive(Debug, Serialize, PartialEq)] +#[serde(tag = "type")] +pub enum IPOption { + EOOL, + NOP, + RR { ptr: u8, route: Vec }, + // MTUP, + // MTUR, + Other { value: u8, len: u8, data: Vec }, +} + #[derive(Debug, Default, Serialize)] pub struct IPv4 { version: u8, hdr_len: u8, tos: u8, len: u16, + #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")] id: u16, + #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u8")] flags: u8, frag_offset: u16, ttl: u8, proto: u8, + #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")] checksum: u16, src_addr: IPv4Address, dst_addr: IPv4Address, - // FIXME: Add options + options: Vec, } impl IPv4 { @@ -73,11 +96,127 @@ impl IPv4 { } } +impl IPv4 { + fn options_from_bytes(&mut self, bytes: &[u8], mut remaining: usize) -> Result { + let mut i = 0_usize; + let mut done = false; + + while !done { + let (option, consumed) = Self::option_from_bytes(&bytes[i..], remaining)?; + i += consumed; + remaining -= consumed; + if remaining == 0 || option == IPOption::EOOL { + done = true; + } + self.options.push(option); + } + + Ok(i) + } + + fn option_from_bytes(bytes: &[u8], mut remaining: usize) -> Result<(IPOption, usize), Error> { + let mut i = 0_usize; + let value = bytes[0]; + + // from: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml + let option = match value { + 0 => { + i += 2; + remaining -= 2; + IPOption::EOOL + } + 1 => { + i += 2; + remaining -= 2; + IPOption::NOP + } + 7 => { + let ((len, data), consumed) = Self::option_data_from_bytes(&bytes[i..], remaining)?; + i += consumed; + remaining -= consumed; + + let ptr = data[0]; + let mut route = Vec::new(); + + { + // ptr within data + let ptr = ptr as usize - 3; + let mut i = 1_usize; + // run till either data is exhausted or we hit ptr + while i + 2 < len as usize && i < ptr { + let addr = data[i..i + 4].try_into().unwrap(); + route.push(addr); + i += 4; + } + } + + IPOption::RR { ptr, route } + } + 11 => { + todo!("MTUP") + } + 12 => { + todo!("MTUR") + } + value => { + let ((len, data), consumed) = Self::option_data_from_bytes(&bytes[i..], remaining)?; + i += consumed; + remaining -= consumed; + + IPOption::Other { + value, + len, + data: data.into(), + } + } + }; + + Ok((option, i)) + } + + fn option_data_from_bytes( + bytes: &[u8], + mut remaining: usize, + ) -> Result<((u8, &[u8]), usize), Error> { + // skip first byte(type) + let mut i = 1_usize; + remaining -= 1; + // check if we have enough bytes for length field + if remaining < 1 { + return Err(Error::TooShort { + required: 1, + available: remaining, + data: hex::encode(bytes), + }); + } + // len also includes the type and len octets + let len = bytes[i] as usize; + i += 1; + remaining -= 1; + + if remaining + 2 < len { + return Err(Error::TooShort { + required: len, + available: remaining + 2, + data: hex::encode(bytes), + }); + } + let data = &bytes[i..i + len - 2]; + i += len - 2; + remaining -= len - 2; + + Ok(((len as u8, data), i)) + } +} + impl Layer for IPv4 { fn decode_bytes( &mut self, bytes: &[u8], ) -> Result<(Option>, usize), Error> { + let mut decoded = 0_usize; + let mut remaining = bytes.len(); + self.version = bytes[0] >> 4; self.hdr_len = bytes[0] & 0x0f; // Length is in 4 octets @@ -89,22 +228,39 @@ impl Layer for IPv4 { }); } self.tos = bytes[1]; - self.len = (bytes[2] as u16) << 8 | (bytes[3] as u16); - self.id = (bytes[4] as u16) << 8 | (bytes[5] as u16); - let flags_offset = (bytes[6] as u16) << 8 | (bytes[7] as u16); + self.len = u16::from_be_bytes(bytes[2..4].try_into().unwrap()); + self.id = u16::from_be_bytes(bytes[4..6].try_into().unwrap()); + let flags_offset = u16::from_be_bytes(bytes[6..8].try_into().unwrap()); self.flags = (flags_offset >> 13) as u8; - self.frag_offset = flags_offset & 0xe0; + self.frag_offset = flags_offset & 0x1fff; self.ttl = bytes[8]; self.proto = bytes[9]; self.checksum = (bytes[10] as u16) << 8 | (bytes[11] as u16); self.src_addr = bytes[12..16].try_into().unwrap(); self.dst_addr = bytes[16..20].try_into().unwrap(); + decoded += IPV4_BASE_HEADER_LENGTH; + remaining -= IPV4_BASE_HEADER_LENGTH; + + // check if enough bytes exist for options + if bytes.len() < self.hdr_len as usize * 4 { + return Err(Error::TooShort { + required: self.hdr_len as usize * 4, + available: bytes.len(), + data: hex::encode(bytes), + }); + } + + let consumed = self.options_from_bytes(&bytes[decoded..], remaining)?; + decoded += consumed; + remaining -= consumed; + let map = PROTOCOLS_MAP.read().unwrap(); let layer = map.get(&self.proto); + match layer { - None => Ok((None, IPV4_BASE_HEADER_LENGTH)), - Some(l4_creator) => Ok((Some(l4_creator()), IPV4_BASE_HEADER_LENGTH)), + None => Ok((None, decoded)), + Some(l4_creator) => Ok((Some(l4_creator()), decoded)), } } diff --git a/src/layers/mod.rs b/src/layers/mod.rs index b288cca..14f99b3 100644 --- a/src/layers/mod.rs +++ b/src/layers/mod.rs @@ -19,7 +19,7 @@ pub mod sctp; pub mod m3ua; -pub mod vxlan; pub mod mpls; +pub mod vxlan; include!(concat!(env!("OUT_DIR"), "/register_defaults.rs")); diff --git a/src/types/ipaddr.rs b/src/types/ipaddr.rs index 11798a7..52539b2 100644 --- a/src/types/ipaddr.rs +++ b/src/types/ipaddr.rs @@ -10,7 +10,7 @@ use serde::{Serialize, Serializer}; use crate::errors::Error as CrateError; -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq)] pub struct IPv4Address([u8; 4]); impl TryFrom<&'_ [u8]> for IPv4Address { @@ -62,7 +62,7 @@ impl Serialize for IPv4Address { } } -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq)] pub struct IPv6Address([u16; 8]); impl TryFrom<&'_ [u8]> for IPv6Address {