From fa0494c9a0c7003bdff86c0b31e340aa2aaeeca0 Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 7 Jun 2020 11:48:01 -0600 Subject: [PATCH 1/9] total WIP of adding ipv4 defrag --- src/layer3/ipv4_defrag.rs | 60 +++++++++++++++++++++++++++++++++++++++ src/layer3/mod.rs | 1 + 2 files changed, 61 insertions(+) create mode 100644 src/layer3/ipv4_defrag.rs diff --git a/src/layer3/ipv4_defrag.rs b/src/layer3/ipv4_defrag.rs new file mode 100644 index 0000000..06c11f5 --- /dev/null +++ b/src/layer3/ipv4_defrag.rs @@ -0,0 +1,60 @@ +use crate::layer3::IPv4; +use std::collections::HashMap; + +pub struct IPv4Defrag<'a>{ + fragments: HashMap>, + max_offset: Option, +} + +struct Flags { + do_not_frag: bool, + more_frags: bool, + frag_offset: u16, +} + +impl Flags { + fn extract_flags(flags: u16) -> Flags { + let frag_offset = flags & 8191; // 0001,1111,1111,1111 + let flags = flags >> 13; + let more_frags = flags & 1 == 1; + let do_not_frag = flags & 2 == 1; + Flags { + do_not_frag, + more_frags, + frag_offset + } + } +} + +impl <'a> IPv4Defrag<'a> { + pub fn new() -> IPv4Defrag<'a> { + IPv4Defrag { + fragments: HashMap::new(), + max_offset: None, + } + } + + + pub fn add_packet(&mut self, ipv4: IPv4<'a>) -> Option> { + let flags = Flags::extract_flags(ipv4.flags); + + + if flags.more_frags { + self.fragments.insert(flags.frag_offset as _, ipv4); + } else if flags.frag_offset != 0 { + self.fragments.insert(flags.frag_offset as _, ipv4); + self.max_offset = Some(flags.frag_offset as _); + } else { // more flags was false and the offset is 0 == no fragmentation + return Some(ipv4); + } + + if let Some(max_ofset) = self.max_offset { + + } + + + + None + + } +} \ No newline at end of file diff --git a/src/layer3/mod.rs b/src/layer3/mod.rs index c544d8b..eda9514 100644 --- a/src/layer3/mod.rs +++ b/src/layer3/mod.rs @@ -1,6 +1,7 @@ pub mod arp; pub mod ipv4; pub mod ipv6; +mod ipv4_defrag; pub use arp::Arp as Arp; pub use ipv4::IPv4 as IPv4; From 3cbb087a42253a7e08a5295b724530ba2eabebeb Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 7 Jun 2020 12:01:36 -0600 Subject: [PATCH 2/9] still needs work on reassembly --- src/layer3/ipv4_defrag.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/layer3/ipv4_defrag.rs b/src/layer3/ipv4_defrag.rs index 06c11f5..387fd6e 100644 --- a/src/layer3/ipv4_defrag.rs +++ b/src/layer3/ipv4_defrag.rs @@ -1,8 +1,8 @@ use crate::layer3::IPv4; -use std::collections::HashMap; +use std::collections::BTreeMap; pub struct IPv4Defrag<'a>{ - fragments: HashMap>, + fragments: BTreeMap>, max_offset: Option, } @@ -49,11 +49,15 @@ impl <'a> IPv4Defrag<'a> { } if let Some(max_ofset) = self.max_offset { - + let expected_fragments = max_ofset / 8; + if self.fragments.len() == expected_fragments { + let mut buf: Vec = vec![0; max_ofset]; + for (_, frag) in self.fragments.into_iter() { + buf.extend_from_slice(frag.payload) + } + } } - - None } From 4cddf5497d14f2bdd773bd4ab87b42342ecd138b Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 7 Jun 2020 15:04:15 -0600 Subject: [PATCH 3/9] untested impl of rfc#815, after the hole component is tested we should be able to take the payloads in order. Right now, I have 2 components: 1 the hole checker, and the other is a btreemap of that will allow us to read packets in order once we know no gaps exist. Perhaps these could be combined, but to make testing easier for now they are seperate. --- src/layer3/ipv4_defrag.rs | 80 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/src/layer3/ipv4_defrag.rs b/src/layer3/ipv4_defrag.rs index 387fd6e..d2ba9e8 100644 --- a/src/layer3/ipv4_defrag.rs +++ b/src/layer3/ipv4_defrag.rs @@ -1,10 +1,8 @@ use crate::layer3::IPv4; use std::collections::BTreeMap; +use nom::lib::std::collections::VecDeque; -pub struct IPv4Defrag<'a>{ - fragments: BTreeMap>, - max_offset: Option, -} +const MAX_LEN: usize = 2 ^ 16 - 1; struct Flags { do_not_frag: bool, @@ -25,11 +23,80 @@ impl Flags { } } } +struct Hole { + start: usize, + end: usize, +} + +impl Default for Hole { + fn default() -> Self { + Hole { + start: 0, + end: MAX_LEN, + } + } +} + +struct Holes { + holes: VecDeque, +} + +impl Default for Holes { + fn default() -> Self { + let mut holes = VecDeque::new(); + holes.push_back(Hole::default()); + Self{ + holes + } + } +} + +impl Holes { + //https://tools.ietf.org/html/rfc815 + fn add(&mut self, frag_start: usize, frag_end: usize, last_frag: bool) -> bool { + loop { + if let Some(hole) = self.holes.pop_front() { + if frag_start > hole.end || frag_end < hole.start { //Steps #2 and #3 + self.holes.push_back(hole); + continue; + } + + if frag_start > hole.start { //Step #5 + let new_hole = Hole { + start: hole.start, + end: frag_end-1, + }; + self.holes.push_back(new_hole); + continue; + } + + if frag_end < hole.end && !last_frag { //Step #6 + let new_hole = Hole { + start: frag_end + 1, + end: hole.end, + }; + self.holes.push_back(new_hole); + } + } else { + return true + } + } + + + + } +} +pub struct IPv4Defrag<'a>{ + holes: Holes, + buffer: BTreeMap>, + max_offset: Option, +} impl <'a> IPv4Defrag<'a> { pub fn new() -> IPv4Defrag<'a> { IPv4Defrag { - fragments: HashMap::new(), + holes: Holes::default(), + buffer: BTreeMap::new(), max_offset: None, } } @@ -38,7 +105,7 @@ impl <'a> IPv4Defrag<'a> { pub fn add_packet(&mut self, ipv4: IPv4<'a>) -> Option> { let flags = Flags::extract_flags(ipv4.flags); - + /* if flags.more_frags { self.fragments.insert(flags.frag_offset as _, ipv4); } else if flags.frag_offset != 0 { @@ -57,6 +124,7 @@ impl <'a> IPv4Defrag<'a> { } } } + */ None From 058494fa1786863eaaa63e1473360e7f1afcd28b Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 7 Jun 2020 18:58:48 -0600 Subject: [PATCH 4/9] tests around holes --- Cargo.toml | 1 + src/layer3/defrag/holes.rs | 128 +++++++++++++++++++ src/layer3/{ipv4_defrag.rs => defrag/mod.rs} | 68 +--------- src/layer3/mod.rs | 2 +- 4 files changed, 134 insertions(+), 65 deletions(-) create mode 100644 src/layer3/defrag/holes.rs rename src/layer3/{ipv4_defrag.rs => defrag/mod.rs} (53%) diff --git a/Cargo.toml b/Cargo.toml index 0a3c5dc..89367fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ env_logger = "0.6" hex-slice = "0.1" regex = "1" hex = "0.3" +rand = "0.7" [[bench]] path = "benches/benches.rs" diff --git a/src/layer3/defrag/holes.rs b/src/layer3/defrag/holes.rs new file mode 100644 index 0000000..71231cf --- /dev/null +++ b/src/layer3/defrag/holes.rs @@ -0,0 +1,128 @@ + +#[derive(Debug)] +struct Hole { + start: usize, + end: usize, +} + +impl Default for Hole { + fn default() -> Self { + Hole { + start: 0, + end: usize::MAX, + } + } +} + +pub struct Holes { + holes: Vec, +} + +impl Default for Holes { + fn default() -> Self { + let mut holes = Vec::new(); + holes.push(Hole::default()); + Self{ + holes + } + } +} + +impl Holes { + //https://tools.ietf.org/html/rfc815 + fn add(&mut self, frag_start: usize, frag_end: usize, last_frag: bool) -> bool { + let mut new_holes = vec![]; + for hole in std::mem::take(&mut self.holes) { + if frag_start > hole.end || frag_end < hole.start { //Steps #2 and #3 + new_holes.push(hole); + continue; + } + if frag_start > hole.start { //Step #5 + let new_hole = Hole { + start: hole.start, + end: frag_start-1, + }; + new_holes.push(new_hole); + } + + if frag_end < hole.end && !last_frag { //Step #6 + let new_hole = Hole { + start: frag_end + 1, + end: hole.end, + }; + new_holes.push(new_hole); + } + } + if new_holes.is_empty() { + true + } else { + self.holes = new_holes; + false + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use rand::seq::SliceRandom; + + fn create_frags() -> Vec<(Hole, bool)> { + let step = 10; + let mut frags = vec![]; + for frag_start in (0..100).step_by(step) { + let frag = Hole{ + start: frag_start, + end: frag_start + step -1, + }; + + frags.push((frag, false)); + } + if let Some((frag, end)) = frags.last_mut() { + *end = true; + } + frags + } + + #[test] + fn handle_sequence_asc() { + let frags = create_frags(); + println!("Frags {:?}", frags); + let mut holes = Holes::default(); + + let result = frags.into_iter().map(|(f, last)| { + holes.add(f.start, f.end, last) + }).collect::>().last().map(|f| *f).unwrap(); + + assert_eq!(result, true); + } + + #[test] + fn handle_sequence_desc() { + let mut frags = create_frags(); + frags.reverse(); + println!("Frags {:?}", frags); + let mut holes = Holes::default(); + + let result = frags.into_iter().map(|(f, last)| { + holes.add(f.start, f.end, last) + }).collect::>().last().map(|f| *f).unwrap(); + + assert_eq!(result, true); + } + + #[test] + fn handle_sequence_random() { + let mut rng = rand::thread_rng(); + let mut frags = create_frags(); + frags.shuffle(&mut rng); + println!("Frags {:?}", frags); + let mut holes = Holes::default(); + + let result = frags.into_iter().map(|(f, last)| { + holes.add(f.start, f.end, last) + }).collect::>().last().map(|f| *f).unwrap(); + + assert_eq!(result, true); + } +} \ No newline at end of file diff --git a/src/layer3/ipv4_defrag.rs b/src/layer3/defrag/mod.rs similarity index 53% rename from src/layer3/ipv4_defrag.rs rename to src/layer3/defrag/mod.rs index d2ba9e8..236aa1e 100644 --- a/src/layer3/ipv4_defrag.rs +++ b/src/layer3/defrag/mod.rs @@ -4,6 +4,8 @@ use nom::lib::std::collections::VecDeque; const MAX_LEN: usize = 2 ^ 16 - 1; +mod holes; + struct Flags { do_not_frag: bool, more_frags: bool, @@ -23,71 +25,9 @@ impl Flags { } } } -struct Hole { - start: usize, - end: usize, -} - -impl Default for Hole { - fn default() -> Self { - Hole { - start: 0, - end: MAX_LEN, - } - } -} - -struct Holes { - holes: VecDeque, -} - -impl Default for Holes { - fn default() -> Self { - let mut holes = VecDeque::new(); - holes.push_back(Hole::default()); - Self{ - holes - } - } -} -impl Holes { - //https://tools.ietf.org/html/rfc815 - fn add(&mut self, frag_start: usize, frag_end: usize, last_frag: bool) -> bool { - loop { - if let Some(hole) = self.holes.pop_front() { - if frag_start > hole.end || frag_end < hole.start { //Steps #2 and #3 - self.holes.push_back(hole); - continue; - } - - if frag_start > hole.start { //Step #5 - let new_hole = Hole { - start: hole.start, - end: frag_end-1, - }; - self.holes.push_back(new_hole); - continue; - } - - if frag_end < hole.end && !last_frag { //Step #6 - let new_hole = Hole { - start: frag_end + 1, - end: hole.end, - }; - self.holes.push_back(new_hole); - } - } else { - return true - } - } - - - - } -} pub struct IPv4Defrag<'a>{ - holes: Holes, + holes: holes::Holes, buffer: BTreeMap>, max_offset: Option, } @@ -95,7 +35,7 @@ pub struct IPv4Defrag<'a>{ impl <'a> IPv4Defrag<'a> { pub fn new() -> IPv4Defrag<'a> { IPv4Defrag { - holes: Holes::default(), + holes: holes::Holes::default(), buffer: BTreeMap::new(), max_offset: None, } diff --git a/src/layer3/mod.rs b/src/layer3/mod.rs index eda9514..0e4faff 100644 --- a/src/layer3/mod.rs +++ b/src/layer3/mod.rs @@ -1,7 +1,7 @@ pub mod arp; pub mod ipv4; pub mod ipv6; -mod ipv4_defrag; +mod defrag; pub use arp::Arp as Arp; pub use ipv4::IPv4 as IPv4; From 5de34ef97898b470463743c66f4decad08b9af5a Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 7 Jun 2020 20:23:31 -0600 Subject: [PATCH 5/9] moved location of defrag --- src/{ => flow}/layer3/defrag/holes.rs | 2 +- src/{ => flow}/layer3/defrag/mod.rs | 31 +++++++++++---------------- src/flow/layer3/mod.rs | 2 ++ src/layer3/ipv4.rs | 2 ++ src/layer3/mod.rs | 1 - 5 files changed, 18 insertions(+), 20 deletions(-) rename src/{ => flow}/layer3/defrag/holes.rs (97%) rename src/{ => flow}/layer3/defrag/mod.rs (62%) diff --git a/src/layer3/defrag/holes.rs b/src/flow/layer3/defrag/holes.rs similarity index 97% rename from src/layer3/defrag/holes.rs rename to src/flow/layer3/defrag/holes.rs index 71231cf..4737133 100644 --- a/src/layer3/defrag/holes.rs +++ b/src/flow/layer3/defrag/holes.rs @@ -30,7 +30,7 @@ impl Default for Holes { impl Holes { //https://tools.ietf.org/html/rfc815 - fn add(&mut self, frag_start: usize, frag_end: usize, last_frag: bool) -> bool { + pub fn add(&mut self, frag_start: usize, frag_end: usize, last_frag: bool) -> bool { let mut new_holes = vec![]; for hole in std::mem::take(&mut self.holes) { if frag_start > hole.end || frag_end < hole.start { //Steps #2 and #3 diff --git a/src/layer3/defrag/mod.rs b/src/flow/layer3/defrag/mod.rs similarity index 62% rename from src/layer3/defrag/mod.rs rename to src/flow/layer3/defrag/mod.rs index 236aa1e..3d4fd41 100644 --- a/src/layer3/defrag/mod.rs +++ b/src/flow/layer3/defrag/mod.rs @@ -1,8 +1,8 @@ use crate::layer3::IPv4; use std::collections::BTreeMap; use nom::lib::std::collections::VecDeque; +use crate::layer3::ipv4::HEADER_LENGTH; -const MAX_LEN: usize = 2 ^ 16 - 1; mod holes; @@ -29,7 +29,6 @@ impl Flags { pub struct IPv4Defrag<'a>{ holes: holes::Holes, buffer: BTreeMap>, - max_offset: Option, } impl <'a> IPv4Defrag<'a> { @@ -37,35 +36,31 @@ impl <'a> IPv4Defrag<'a> { IPv4Defrag { holes: holes::Holes::default(), buffer: BTreeMap::new(), - max_offset: None, } } + fn extract_range_from_flags(flags: &Flags, total_len: usize) -> (usize, usize) { + let start = flags.frag_offset as usize * 8; + let end = start + (total_len - HEADER_LENGTH); + (start, end) + } + pub fn add_packet(&mut self, ipv4: IPv4<'a>) -> Option> { let flags = Flags::extract_flags(ipv4.flags); - /* if flags.more_frags { - self.fragments.insert(flags.frag_offset as _, ipv4); + let (start, end) = Self::extract_range_from_flags(&flags, ipv4.raw_length as _); + self.buffer.insert(flags.frag_offset as _, ipv4); + self.holes.add(start, end, false); } else if flags.frag_offset != 0 { - self.fragments.insert(flags.frag_offset as _, ipv4); - self.max_offset = Some(flags.frag_offset as _); + let (start, end) = Self::extract_range_from_flags(&flags, ipv4.raw_length as _); + self.buffer.insert(flags.frag_offset as _, ipv4); + self.holes.add(start, end, true); } else { // more flags was false and the offset is 0 == no fragmentation return Some(ipv4); } - if let Some(max_ofset) = self.max_offset { - let expected_fragments = max_ofset / 8; - if self.fragments.len() == expected_fragments { - let mut buf: Vec = vec![0; max_ofset]; - for (_, frag) in self.fragments.into_iter() { - buf.extend_from_slice(frag.payload) - } - } - } - */ - None } diff --git a/src/flow/layer3/mod.rs b/src/flow/layer3/mod.rs index 40d7a6c..33645da 100644 --- a/src/flow/layer3/mod.rs +++ b/src/flow/layer3/mod.rs @@ -2,6 +2,8 @@ pub mod arp; pub mod ipv4; pub mod ipv6; +mod defrag; + use crate::flow::Flow; use crate::flow::errors::Error; use crate::flow::info::layer2::Info; diff --git a/src/layer3/ipv4.rs b/src/layer3/ipv4.rs index 88633c7..526a644 100644 --- a/src/layer3/ipv4.rs +++ b/src/layer3/ipv4.rs @@ -11,6 +11,8 @@ use std::io::{Cursor, Write}; const ADDRESS_LENGTH: usize = 4; +pub const HEADER_LENGTH: usize = 20; + #[derive(Clone, Copy, Debug)] pub struct IPv4<'a> { pub version_and_length: u8, diff --git a/src/layer3/mod.rs b/src/layer3/mod.rs index 0e4faff..c544d8b 100644 --- a/src/layer3/mod.rs +++ b/src/layer3/mod.rs @@ -1,7 +1,6 @@ pub mod arp; pub mod ipv4; pub mod ipv6; -mod defrag; pub use arp::Arp as Arp; pub use ipv4::IPv4 as IPv4; From 96f7ee6fb550f98aab6eee38ca73a6d24ee4c63d Mon Sep 17 00:00:00 2001 From: Ben K Date: Mon, 8 Jun 2020 23:06:39 -0600 Subject: [PATCH 6/9] wip on reassembly --- src/flow/layer3/defrag/mod.rs | 31 +++++++++++++++++----------- src/flow/layer3/defrag/reassemble.rs | 18 ++++++++++++++++ src/flow/layer3/ipv4.rs | 2 ++ 3 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/flow/layer3/defrag/reassemble.rs diff --git a/src/flow/layer3/defrag/mod.rs b/src/flow/layer3/defrag/mod.rs index 3d4fd41..a23de59 100644 --- a/src/flow/layer3/defrag/mod.rs +++ b/src/flow/layer3/defrag/mod.rs @@ -3,8 +3,8 @@ use std::collections::BTreeMap; use nom::lib::std::collections::VecDeque; use crate::layer3::ipv4::HEADER_LENGTH; - mod holes; +mod reassemble; struct Flags { do_not_frag: bool, @@ -26,14 +26,14 @@ impl Flags { } } -pub struct IPv4Defrag<'a>{ +pub struct IPDefragSession<'a>{ holes: holes::Holes, buffer: BTreeMap>, } -impl <'a> IPv4Defrag<'a> { - pub fn new() -> IPv4Defrag<'a> { - IPv4Defrag { +impl <'a> IPDefragSession<'a> { + pub fn new() -> IPDefragSession<'a> { + IPDefragSession { holes: holes::Holes::default(), buffer: BTreeMap::new(), } @@ -45,20 +45,27 @@ impl <'a> IPv4Defrag<'a> { (start, end) } - pub fn add_packet(&mut self, ipv4: IPv4<'a>) -> Option> { let flags = Flags::extract_flags(ipv4.flags); - if flags.more_frags { + if !flags.more_frags && flags.frag_offset == 0 { + return Some(ipv4); + } + + let complete_session = if flags.more_frags { let (start, end) = Self::extract_range_from_flags(&flags, ipv4.raw_length as _); self.buffer.insert(flags.frag_offset as _, ipv4); - self.holes.add(start, end, false); - } else if flags.frag_offset != 0 { + self.holes.add(start, end, false) + } else { let (start, end) = Self::extract_range_from_flags(&flags, ipv4.raw_length as _); self.buffer.insert(flags.frag_offset as _, ipv4); - self.holes.add(start, end, true); - } else { // more flags was false and the offset is 0 == no fragmentation - return Some(ipv4); + self.holes.add(start, end, true) + }; + + if complete_session { + let buffer = std::mem::take(&mut self.buffer).into_iter().map(|t|t.1).collect::>(); + reassemble::ipv4(buffer); + //re-assemble } None diff --git a/src/flow/layer3/defrag/reassemble.rs b/src/flow/layer3/defrag/reassemble.rs new file mode 100644 index 0000000..ffe1b79 --- /dev/null +++ b/src/flow/layer3/defrag/reassemble.rs @@ -0,0 +1,18 @@ +use crate::layer3::IPv4; +use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; + +pub fn ipv4<'a>(buffer: Vec>) -> Result { + let mut buffer = buffer; + if buffer.is_empty() { + return Err(Ipv4Error::Defrag{msg: String::from("empty buffer")}) + } + let mut first = buffer.remove(0); + let mut payload = Vec::new(); + payload.extend_from_slice(first.payload); + + for packet in buffer.into_iter() { + payload.extend_from_slice(packet.payload); + } + std::mem::replace(&mut first.payload, payload.as_slice()); + Ok(first) +} \ No newline at end of file diff --git a/src/flow/layer3/ipv4.rs b/src/flow/layer3/ipv4.rs index 82ff107..a62dec3 100644 --- a/src/flow/layer3/ipv4.rs +++ b/src/flow/layer3/ipv4.rs @@ -31,6 +31,8 @@ pub mod errors { InternetProtocolId { id: InternetProtocolId }, + #[fail(display = "DefragErrorIPv4: {:?}", msg)] + Defrag{msg: String}, } unsafe impl Sync for Error {} From 689a5896a4be1e1979108e69aab11c04dea56dc3 Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 21 Jun 2020 22:46:14 -0600 Subject: [PATCH 7/9] some work to make the ipv4 payload a enum, so that we can copy need be in case of fragments --- Cargo.toml | 1 + src/flow/layer3/defrag/mod.rs | 34 ++++++--------- src/flow/layer3/defrag/reassemble.rs | 36 ++++++++-------- src/flow/layer3/ipv4.rs | 8 ++-- src/flow/layer3/mod.rs | 2 +- src/layer3/ipv4.rs | 64 ++++++++++++++++++++++++++-- src/layer3/mod.rs | 3 +- src/layer4/vxlan.rs | 4 +- 8 files changed, 102 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89367fa..e60077e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ categories=["encoding","network-programming","parsing"] [dependencies] arrayref = "0.3" +bytes = "0.5.5" byteorder = "1.3" nom = "4" failure = "0.1" diff --git a/src/flow/layer3/defrag/mod.rs b/src/flow/layer3/defrag/mod.rs index a23de59..b745d7c 100644 --- a/src/flow/layer3/defrag/mod.rs +++ b/src/flow/layer3/defrag/mod.rs @@ -1,34 +1,27 @@ -use crate::layer3::IPv4; -use std::collections::BTreeMap; -use nom::lib::std::collections::VecDeque; +use crate::layer3::{IPv4, IPv4Flags}; +use std::collections::{BTreeMap, HashMap}; use crate::layer3::ipv4::HEADER_LENGTH; mod holes; mod reassemble; -struct Flags { - do_not_frag: bool, - more_frags: bool, - frag_offset: u16, +pub struct IPDefrag<'a> { + sessions: HashMap> } -impl Flags { - fn extract_flags(flags: u16) -> Flags { - let frag_offset = flags & 8191; // 0001,1111,1111,1111 - let flags = flags >> 13; - let more_frags = flags & 1 == 1; - let do_not_frag = flags & 2 == 1; - Flags { - do_not_frag, - more_frags, - frag_offset +impl<'a> Default for IPDefrag<'a> { + fn default() -> Self { + IPDefrag { + sessions: HashMap::new(), } + } } pub struct IPDefragSession<'a>{ holes: holes::Holes, buffer: BTreeMap>, + reassembly_buffer: Vec<&'a [u8]> } impl <'a> IPDefragSession<'a> { @@ -36,17 +29,18 @@ impl <'a> IPDefragSession<'a> { IPDefragSession { holes: holes::Holes::default(), buffer: BTreeMap::new(), + reassembly_buffer: vec![], } } - fn extract_range_from_flags(flags: &Flags, total_len: usize) -> (usize, usize) { + fn extract_range_from_flags(flags: &IPv4Flags, total_len: usize) -> (usize, usize) { let start = flags.frag_offset as usize * 8; let end = start + (total_len - HEADER_LENGTH); (start, end) } pub fn add_packet(&mut self, ipv4: IPv4<'a>) -> Option> { - let flags = Flags::extract_flags(ipv4.flags); + let flags = IPv4Flags::extract_flags(ipv4.flags); if !flags.more_frags && flags.frag_offset == 0 { return Some(ipv4); @@ -64,7 +58,7 @@ impl <'a> IPDefragSession<'a> { if complete_session { let buffer = std::mem::take(&mut self.buffer).into_iter().map(|t|t.1).collect::>(); - reassemble::ipv4(buffer); + //reassemble::ipv4(buffer); //re-assemble } diff --git a/src/flow/layer3/defrag/reassemble.rs b/src/flow/layer3/defrag/reassemble.rs index ffe1b79..daa2a5a 100644 --- a/src/flow/layer3/defrag/reassemble.rs +++ b/src/flow/layer3/defrag/reassemble.rs @@ -1,18 +1,18 @@ -use crate::layer3::IPv4; -use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; - -pub fn ipv4<'a>(buffer: Vec>) -> Result { - let mut buffer = buffer; - if buffer.is_empty() { - return Err(Ipv4Error::Defrag{msg: String::from("empty buffer")}) - } - let mut first = buffer.remove(0); - let mut payload = Vec::new(); - payload.extend_from_slice(first.payload); - - for packet in buffer.into_iter() { - payload.extend_from_slice(packet.payload); - } - std::mem::replace(&mut first.payload, payload.as_slice()); - Ok(first) -} \ No newline at end of file +// use crate::layer3::IPv4; +// use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; +// +// pub fn ipv4<'a>(buffer: Vec>) -> Result { +// let mut buffer = buffer; +// if buffer.is_empty() { +// return Err(Ipv4Error::Defrag{msg: String::from("empty buffer")}) +// } +// let mut first = buffer.remove(0); +// let mut payload = Vec::new(); +// payload.extend_from_slice(first.payload); +// +// for packet in buffer.into_iter() { +// payload.extend_from_slice(packet.payload); +// } +// std::mem::replace(&mut first.payload, payload.as_slice()); +// Ok(first) +//} \ No newline at end of file diff --git a/src/flow/layer3/ipv4.rs b/src/flow/layer3/ipv4.rs index a62dec3..e7fe139 100644 --- a/src/flow/layer3/ipv4.rs +++ b/src/flow/layer3/ipv4.rs @@ -31,8 +31,8 @@ pub mod errors { InternetProtocolId { id: InternetProtocolId }, - #[fail(display = "DefragErrorIPv4: {:?}", msg)] - Defrag{msg: String}, + #[fail(display = "Fragment")] + Fragment, } unsafe impl Sync for Error {} @@ -50,7 +50,7 @@ impl<'a> FlowExtraction for IPv4<'a> { let proto = self.protocol.clone(); match proto { InternetProtocolId::Tcp => { - Tcp::parse(self.payload).map_err(|e| { + Tcp::parse(&self.payload).map_err(|e| { error!("Error parsing tcp {:?}", e); let e: L3Error = errors::Error::NetParser { l4: proto.clone(), @@ -72,7 +72,7 @@ impl<'a> FlowExtraction for IPv4<'a> { }) } InternetProtocolId::Udp => { - Udp::parse(self.payload).map_err(|e| { + Udp::parse(&self.payload).map_err(|e| { #[cfg(feature = "logging")] error!("Error parsing udp {:?}", e); let e: L3Error = errors::Error::NetParser { diff --git a/src/flow/layer3/mod.rs b/src/flow/layer3/mod.rs index 33645da..f8b7e7a 100644 --- a/src/flow/layer3/mod.rs +++ b/src/flow/layer3/mod.rs @@ -2,7 +2,7 @@ pub mod arp; pub mod ipv4; pub mod ipv6; -mod defrag; +pub mod defrag; use crate::flow::Flow; use crate::flow::errors::Error; diff --git a/src/layer3/ipv4.rs b/src/layer3/ipv4.rs index 526a644..0aef1b1 100644 --- a/src/layer3/ipv4.rs +++ b/src/layer3/ipv4.rs @@ -8,12 +8,40 @@ use nom::*; use std::mem::size_of; use std::net::IpAddr; use std::io::{Cursor, Write}; +use failure::_core::ops::Deref; const ADDRESS_LENGTH: usize = 4; pub const HEADER_LENGTH: usize = 20; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] +pub enum Payload<'a> { + Slice(&'a [u8]), + Owned(Vec), +} + +impl <'a> Deref for Payload<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl <'a> Payload<'a> { + pub fn as_slice(&self) -> &[u8] { + match self { + Payload::Slice(s) => { + *s + }, + Payload::Owned(v) => { + v.as_slice() + } + } + } +} + +#[derive(Clone, Debug)] pub struct IPv4<'a> { pub version_and_length: u8, pub tos: u8, @@ -25,7 +53,7 @@ pub struct IPv4<'a> { pub checksum: u16, pub src_ip: IpAddr, pub dst_ip: IpAddr, - pub payload: &'a [u8], + pub payload: Payload<'a>, pub options: Option<&'a [u8]>, pub padding: Option<&'a [u8]>, } @@ -65,7 +93,7 @@ impl<'a> IPv4<'a> { if let IpAddr::V4(v) = self.dst_ip { writer.write(&v.octets()).unwrap(); } - writer.write(self.payload).unwrap(); + writer.write(&self.payload).unwrap(); if let Some(i) = self.options { writer.write(i).unwrap(); } @@ -122,7 +150,7 @@ impl<'a> IPv4<'a> { >> checksum: be_u16 >> src_ip: ipv4_address >> dst_ip: ipv4_address - >> payload: take!(length) + >> payload: map!(take!(length), Payload::Slice) >> options: cond!(additional_length > 0, take!(additional_length)) >> padding: cond!( @@ -160,6 +188,34 @@ impl<'a> IPv4<'a> { } }) } + + pub fn flags(&self) -> Flags { + Flags::extract_flags(self.flags) + } +} + +pub struct Flags { + pub do_not_frag: bool, + pub more_frags: bool, + pub frag_offset: u16, +} + +impl Flags { + pub fn extract_flags(flags: u16) -> Flags { + let frag_offset = flags & 8191; // 0001,1111,1111,1111 + let flags = flags >> 13; + let more_frags = flags & 1 == 1; + let do_not_frag = flags & 2 == 1; + Flags { + do_not_frag, + more_frags, + frag_offset + } + } + + pub fn is_fragment(&self) -> bool { + self.more_frags || self.frag_offset != 0 + } } #[cfg(test)] diff --git a/src/layer3/mod.rs b/src/layer3/mod.rs index c544d8b..00e90e7 100644 --- a/src/layer3/mod.rs +++ b/src/layer3/mod.rs @@ -4,6 +4,7 @@ pub mod ipv6; pub use arp::Arp as Arp; pub use ipv4::IPv4 as IPv4; +pub use ipv4::Flags as IPv4Flags; pub use ipv6::IPv6 as IPv6; use log::*; @@ -11,7 +12,7 @@ use log::*; /// /// Available layer 3 representations /// -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub enum Layer3<'a> { Arp(Arp), IPv4(IPv4<'a>), diff --git a/src/layer4/vxlan.rs b/src/layer4/vxlan.rs index 7407154..ec83b5d 100644 --- a/src/layer4/vxlan.rs +++ b/src/layer4/vxlan.rs @@ -91,7 +91,7 @@ mod tests { let ip: IPv4 = IPv4::parse(enet.payload).expect("Invalid IPv4").1; assert_eq!(format!("{}", ip.dst_ip), "192.168.56.12"); - let udp: Udp = Udp::parse(ip.payload).expect("Invalid udp").1; + let udp: Udp = Udp::parse(&ip.payload).expect("Invalid udp").1; assert_eq!(udp.dst_port, 4789); let (remainder, vxlan) = Vxlan::parse(&udp.payload, nom::Endianness::Big).expect("Invalid VXLAN"); @@ -129,7 +129,7 @@ mod tests { let ip: IPv4 = IPv4::parse(enet.payload).expect("Invalid IPv4").1; assert_eq!(format!("{}", ip.dst_ip), "1.1.1.1"); - let udp: Udp = Udp::parse(ip.payload).expect("Invalid udp").1; + let udp: Udp = Udp::parse(&ip.payload).expect("Invalid udp").1; assert_eq!(udp.dst_port, 5300); let vxlan = Vxlan::parse(&udp.payload, nom::Endianness::Big); From d853eb3ee3379dbd015a61e890c0773788d9132b Mon Sep 17 00:00:00 2001 From: Ben K Date: Sun, 21 Jun 2020 22:53:20 -0600 Subject: [PATCH 8/9] clean up reassembly --- src/flow/layer3/defrag/reassemble.rs | 34 +++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/flow/layer3/defrag/reassemble.rs b/src/flow/layer3/defrag/reassemble.rs index daa2a5a..319d8c6 100644 --- a/src/flow/layer3/defrag/reassemble.rs +++ b/src/flow/layer3/defrag/reassemble.rs @@ -1,18 +1,16 @@ -// use crate::layer3::IPv4; -// use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; -// -// pub fn ipv4<'a>(buffer: Vec>) -> Result { -// let mut buffer = buffer; -// if buffer.is_empty() { -// return Err(Ipv4Error::Defrag{msg: String::from("empty buffer")}) -// } -// let mut first = buffer.remove(0); -// let mut payload = Vec::new(); -// payload.extend_from_slice(first.payload); -// -// for packet in buffer.into_iter() { -// payload.extend_from_slice(packet.payload); -// } -// std::mem::replace(&mut first.payload, payload.as_slice()); -// Ok(first) -//} \ No newline at end of file +use crate::layer3::IPv4; +use crate::layer3::ipv4::Payload; +use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; + +pub fn ipv4<'a>(first: IPv4<'a>, buffer: Vec>) -> Result, Ipv4Error> { + let mut buffer = buffer; + let mut first = first; + let mut payload = Vec::new(); + payload.extend_from_slice(&first.payload); + + for packet in buffer.into_iter() { + payload.extend_from_slice(&packet.payload); + } + first.payload = Payload::Owned(payload); + Ok(first) +} \ No newline at end of file From 8742db4a55c0f468eacab4308c54e52722092f2b Mon Sep 17 00:00:00 2001 From: Ben K Date: Mon, 22 Jun 2020 19:55:10 -0600 Subject: [PATCH 9/9] still mucking around bytes and slices... ownership is a bitch --- src/flow/layer3/defrag/reassemble.rs | 4 ++-- src/flow/layer3/mod.rs | 5 +++++ src/layer3/ipv4.rs | 30 ++++++++++++++-------------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/flow/layer3/defrag/reassemble.rs b/src/flow/layer3/defrag/reassemble.rs index 319d8c6..5b1d5db 100644 --- a/src/flow/layer3/defrag/reassemble.rs +++ b/src/flow/layer3/defrag/reassemble.rs @@ -1,5 +1,5 @@ use crate::layer3::IPv4; -use crate::layer3::ipv4::Payload; +use crate::layer3::ipv4::Bytes; use crate::flow::layer3::ipv4::errors::{Error as Ipv4Error}; pub fn ipv4<'a>(first: IPv4<'a>, buffer: Vec>) -> Result, Ipv4Error> { @@ -11,6 +11,6 @@ pub fn ipv4<'a>(first: IPv4<'a>, buffer: Vec>) -> Result, Ipv4 for packet in buffer.into_iter() { payload.extend_from_slice(&packet.payload); } - first.payload = Payload::Owned(payload); + first.payload = Bytes::Owned(payload); Ok(first) } \ No newline at end of file diff --git a/src/flow/layer3/mod.rs b/src/flow/layer3/mod.rs index f8b7e7a..f5454d2 100644 --- a/src/flow/layer3/mod.rs +++ b/src/flow/layer3/mod.rs @@ -7,6 +7,11 @@ pub mod defrag; use crate::flow::Flow; use crate::flow::errors::Error; use crate::flow::info::layer2::Info; +use crate::flow::layer3::defrag::IPDefrag; + +pub struct FlowContext<'a> { + ip_defrag: Option> +} pub trait FlowExtraction { fn extract_flow(&self, l2: Info) -> Result; diff --git a/src/layer3/ipv4.rs b/src/layer3/ipv4.rs index 0aef1b1..37dcb09 100644 --- a/src/layer3/ipv4.rs +++ b/src/layer3/ipv4.rs @@ -15,12 +15,12 @@ const ADDRESS_LENGTH: usize = 4; pub const HEADER_LENGTH: usize = 20; #[derive(Clone, Debug)] -pub enum Payload<'a> { +pub enum Bytes<'a> { Slice(&'a [u8]), Owned(Vec), } -impl <'a> Deref for Payload<'a> { +impl <'a> Deref for Bytes<'a> { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -28,13 +28,13 @@ impl <'a> Deref for Payload<'a> { } } -impl <'a> Payload<'a> { +impl <'a> Bytes<'a> { pub fn as_slice(&self) -> &[u8] { match self { - Payload::Slice(s) => { + Bytes::Slice(s) => { *s }, - Payload::Owned(v) => { + Bytes::Owned(v) => { v.as_slice() } } @@ -53,9 +53,9 @@ pub struct IPv4<'a> { pub checksum: u16, pub src_ip: IpAddr, pub dst_ip: IpAddr, - pub payload: Payload<'a>, - pub options: Option<&'a [u8]>, - pub padding: Option<&'a [u8]>, + pub payload: Bytes<'a>, + pub options: Option>, + pub padding: Option>, } fn to_ip_address(i: &[u8]) -> IpAddr { @@ -75,8 +75,8 @@ impl<'a> IPv4<'a> { + size_of::() * 4 + 4 * 2 + self.payload.len() - + self.options.map(|i| i.len()).unwrap_or(0) - + self.padding.map(|i| i.len()).unwrap_or(0) + + self.options.iter().map(|i| i.len()).next().unwrap_or(0) + + self.padding.iter().map(|i| i.len()).next().unwrap_or(0) ); let mut writer = Cursor::new(inner); writer.write_u8(self.version_and_length).unwrap(); @@ -94,10 +94,10 @@ impl<'a> IPv4<'a> { writer.write(&v.octets()).unwrap(); } writer.write(&self.payload).unwrap(); - if let Some(i) = self.options { + if let Some(ref i) = self.options { writer.write(i).unwrap(); } - if let Some(i) = self.padding { + if let Some(ref i) = self.padding { writer.write(i).unwrap(); } writer.into_inner() @@ -150,12 +150,12 @@ impl<'a> IPv4<'a> { >> checksum: be_u16 >> src_ip: ipv4_address >> dst_ip: ipv4_address - >> payload: map!(take!(length), Payload::Slice) - >> options: cond!(additional_length > 0, take!(additional_length)) + >> payload: map!(take!(length), Bytes::Slice) + >> options: cond!(additional_length > 0, map!(take!(additional_length), Bytes::Slice)) >> padding: cond!( input_length > expected_length, - take!(input_length - expected_length) + map!(take!(input_length - expected_length), Bytes::Slice) ) >> (IPv4 { version_and_length,