Skip to content

Commit

Permalink
Implement IPv4 options: EOOL, NOP and RR
Browse files Browse the repository at this point in the history
- 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 <aadilshabier1@gmail.com>
  • Loading branch information
aadilshabier committed Mar 5, 2024
1 parent 2436d86 commit f54739f
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 10 deletions.
170 changes: 163 additions & 7 deletions src/layers/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>,
}

#[derive(Debug, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum IPOption {
EOOL,
NOP,
RR { ptr: u8, route: Vec<IPv4Address> },
// MTUP,
// MTUR,
Other { value: u8, len: u8, data: Vec<u8> },
}

#[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<IPOption>,
}

impl IPv4 {
Expand All @@ -73,11 +96,127 @@ impl IPv4 {
}
}

impl IPv4 {
fn options_from_bytes(&mut self, bytes: &[u8], mut remaining: usize) -> Result<usize, Error> {
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<Box<dyn Layer + Send>>, 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
Expand All @@ -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)),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/layers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
4 changes: 2 additions & 2 deletions src/types/ipaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit f54739f

Please sign in to comment.