From 12385b143dcf378e78a624119bd237146c6c3057 Mon Sep 17 00:00:00 2001 From: Johan Mazel Date: Tue, 30 Jul 2024 17:33:46 +0200 Subject: [PATCH] extend ctx use in parsing function and add ipv4 l3 payload parsing function --- pcap-rewrite/Cargo.toml | 1 + pcap-rewrite/src/filters/dispatch_filter.rs | 8 +-- pcap-rewrite/src/filters/filter.rs | 4 +- .../fragmentation/fragmentation_filter.rs | 26 +++++--- .../fragmentation/fragmentation_test.rs | 12 ++-- pcap-rewrite/src/filters/key_parser_ipv4.rs | 61 +++++++++++-------- pcap-rewrite/src/filters/key_parser_ipv6.rs | 24 +++++--- pcap-rewrite/src/rewriter.rs | 2 +- 8 files changed, 83 insertions(+), 55 deletions(-) diff --git a/pcap-rewrite/Cargo.toml b/pcap-rewrite/Cargo.toml index a15b92b..c42b562 100644 --- a/pcap-rewrite/Cargo.toml +++ b/pcap-rewrite/Cargo.toml @@ -32,6 +32,7 @@ doc = false [dependencies] csv = "1.1.6" clap = { version = "4.4", features = ["cargo", "derive"] } +libpcap-analyzer = { version="0.1.0", path="../libpcap-analyzer" } libpcap-tools = { version="0.1.0", path="../libpcap-tools" } log = { version = "0.4" } flate2 = { version = "1.0", features = ["zlib"], default-features = false } diff --git a/pcap-rewrite/src/filters/dispatch_filter.rs b/pcap-rewrite/src/filters/dispatch_filter.rs index fe2c5b6..b257de7 100644 --- a/pcap-rewrite/src/filters/dispatch_filter.rs +++ b/pcap-rewrite/src/filters/dispatch_filter.rs @@ -1,7 +1,7 @@ +use log::warn; use std::io; use std::net::IpAddr; use std::path::Path; -use log::warn; use libpcap_tools::{Error, FiveTuple, ParseContext}; use pcap_parser::data::PacketData; @@ -20,7 +20,7 @@ use crate::filters::key_parser_ipv4; use crate::filters::key_parser_ipv6; /// Function to extract key from data -pub type GetKeyFn = Box Result>; +pub type GetKeyFn = Box Result>; /// Function to keep/drop extract key from container pub type KeepFn = Box Result>; @@ -67,8 +67,8 @@ impl DispatchFilter { PacketData::L3(l3_layer_value_u8, data) => { let ether_type = EtherType::new(l3_layer_value_u8); match ether_type { - EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(data)?, - EtherTypes::Ipv6 => (self.get_key_from_ipv4_l3_data)(data)?, + EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(ctx, data)?, + EtherTypes::Ipv6 => (self.get_key_from_ipv4_l3_data)(ctx, data)?, _ => { warn!( "Unimplemented Ethertype in L3 {:?}/{:x}", diff --git a/pcap-rewrite/src/filters/filter.rs b/pcap-rewrite/src/filters/filter.rs index b3b6aac..7c98ea6 100644 --- a/pcap-rewrite/src/filters/filter.rs +++ b/pcap-rewrite/src/filters/filter.rs @@ -1,4 +1,4 @@ -use libpcap_tools::{Error,Packet,ParseContext}; +use libpcap_tools::{Error, Packet, ParseContext}; use pcap_parser::data::PacketData; /// Verdict emitted by a Filter @@ -25,7 +25,7 @@ pub trait Filter { /// Any error raised in this function is fatal /// /// Note: packet content can be accessed in `packet.data` - fn pre_analyze(&mut self, _packet: &Packet) -> Result<(), Error> { + fn pre_analyze(&mut self, _ctx: &ParseContext, _packet: &Packet) -> Result<(), Error> { Ok(()) } diff --git a/pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs b/pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs index b78da9e..8204191 100644 --- a/pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs +++ b/pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs @@ -29,7 +29,7 @@ use super::convert_fn; /// Function to convert TwoTupleProtoIpid/FiveTuple data to key container pub type ConvertFn = Box) -> Container>; /// Function to extract key from data -pub type GetKeyFn = Box Result>; +pub type GetKeyFn = Box Result>; /// Function to keep/drop extract key from container pub type KeepFn = Box Result>; @@ -64,7 +64,11 @@ impl FragmentationFilter { } } - fn test_fragmentation_and_save(&mut self, packet: &Packet) -> Result<(), Error> { + fn test_fragmentation_and_save( + &mut self, + ctx: &ParseContext, + packet: &Packet, + ) -> Result<(), Error> { // Note: we only test the first fragment to be sure to capture the IP ID value. // Subsequent fragment with TCP/UDP/ICMP are always dropped because header parsing fails on all packets/fragments after the first. let is_first_fragment = match packet.data { @@ -74,6 +78,7 @@ impl FragmentationFilter { } filter_utils::extract_callback_ethernet( + ctx, &fragmentation_test::is_ipv4_first_fragment, &fragmentation_test::is_ipv6_first_fragment, data, @@ -82,8 +87,8 @@ impl FragmentationFilter { PacketData::L3(l3_layer_value_u8, data) => { let ether_type = EtherType::new(l3_layer_value_u8); match ether_type { - EtherTypes::Ipv4 => (fragmentation_test::is_ipv4_first_fragment)(data)?, - EtherTypes::Ipv6 => (fragmentation_test::is_ipv6_first_fragment)(data)?, + EtherTypes::Ipv4 => (fragmentation_test::is_ipv4_first_fragment)(ctx, data)?, + EtherTypes::Ipv6 => (fragmentation_test::is_ipv6_first_fragment)(ctx, data)?, _ => { warn!( "Unimplemented Ethertype in L3: {:?}/{:x}", @@ -105,6 +110,7 @@ impl FragmentationFilter { } Some(filter_utils::extract_callback_ethernet( + ctx, &key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple, &key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple, data, @@ -114,10 +120,10 @@ impl FragmentationFilter { let ether_type = EtherType::new(l3_layer_value_u8); match ether_type { EtherTypes::Ipv4 => Some( - (key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple)(data)?, + (key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple)(ctx, data)?, ), EtherTypes::Ipv6 => Some( - (key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple)(data)?, + (key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple)(ctx, data)?, ), _ => { warn!( @@ -161,8 +167,8 @@ impl FragmentationFilter { PacketData::L3(l3_layer_value_u8, data) => { let ether_type = EtherType::new(l3_layer_value_u8); match ether_type { - EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(data)?, - EtherTypes::Ipv6 => (self.get_key_from_ipv6_l3_data)(data)?, + EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(ctx, data)?, + EtherTypes::Ipv6 => (self.get_key_from_ipv6_l3_data)(ctx, data)?, _ => { warn!( "Unimplemented Ethertype in L3: {:?}/{:x}", @@ -198,8 +204,8 @@ impl Filter for FragmentationFilter { true } - fn pre_analyze(&mut self, _packet: &Packet) -> Result<(), Error> { - self.test_fragmentation_and_save(_packet) + fn pre_analyze(&mut self, ctx: &ParseContext, packet: &Packet) -> Result<(), Error> { + self.test_fragmentation_and_save(ctx, packet) } fn preanalysis_done(&mut self) -> Result<(), Error> { diff --git a/pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs b/pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs index 467fbb9..e463a6d 100644 --- a/pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs +++ b/pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs @@ -1,12 +1,13 @@ -use libpcap_tools::Error; +use libpcap_tools::{Error, ParseContext}; use pnet_packet::ipv4::Ipv4Packet; use pnet_packet::ipv6::Ipv6Packet; use crate::filters::ipv6_utils; -pub fn is_ipv4_first_fragment(payload: &[u8]) -> Result { - let ipv4_packet = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; +pub fn is_ipv4_first_fragment(_ctx: &ParseContext, payload: &[u8]) -> Result { + let ipv4_packet = + Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; let flags = ipv4_packet.get_flags(); let fragment_offset = ipv4_packet.get_fragment_offset(); @@ -15,8 +16,9 @@ pub fn is_ipv4_first_fragment(payload: &[u8]) -> Result { Ok(mf_flag == 1 && fragment_offset == 0) } -pub fn is_ipv6_first_fragment(payload: &[u8]) -> Result { - let ipv6_packet = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; +pub fn is_ipv6_first_fragment(_ctx: &ParseContext, payload: &[u8]) -> Result { + let ipv6_packet = + Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; let (fragment_packet_option, _l4_proto, _payload) = ipv6_utils::get_fragment_packet_option_l4_protol4_payload(payload, &ipv6_packet)?; match fragment_packet_option { diff --git a/pcap-rewrite/src/filters/key_parser_ipv4.rs b/pcap-rewrite/src/filters/key_parser_ipv4.rs index f63ad82..51afed1 100644 --- a/pcap-rewrite/src/filters/key_parser_ipv4.rs +++ b/pcap-rewrite/src/filters/key_parser_ipv4.rs @@ -7,24 +7,27 @@ use pnet_packet::tcp::TcpPacket; use pnet_packet::udp::UdpPacket; use pnet_packet::Packet; -use libpcap_tools::{Error, FiveTuple}; +use libpcap_tools::{Error, FiveTuple, ParseContext}; use super::fragmentation::two_tuple_proto_ipid::TwoTupleProtoIpid; use super::fragmentation::two_tuple_proto_ipid_five_tuple::TwoTupleProtoIpidFiveTuple; -pub fn parse_src_ipaddr(payload: &[u8]) -> Result { +pub fn parse_src_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result { let ipv4 = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; Result::Ok(IpAddr::V4(ipv4.get_source())) } -pub fn parse_dst_ipaddr(payload: &[u8]) -> Result { +pub fn parse_dst_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result { let ipv4 = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; Result::Ok(IpAddr::V4(ipv4.get_destination())) } -pub fn parse_src_dst_ipaddr(payload: &[u8]) -> Result<(IpAddr, IpAddr), Error> { +pub fn parse_src_dst_ipaddr( + _ctx: &ParseContext, + payload: &[u8], +) -> Result<(IpAddr, IpAddr), Error> { let ipv4_packet = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; let src_ipaddr = IpAddr::V4(ipv4_packet.get_source()); @@ -33,6 +36,7 @@ pub fn parse_src_dst_ipaddr(payload: &[u8]) -> Result<(IpAddr, IpAddr), Error> { } pub fn parse_src_ipaddr_proto_dst_port( + ctx: &ParseContext, payload: &[u8], ) -> Result<(IpAddr, IpNextHeaderProtocol, u16), Error> { let ipv4_packet = @@ -42,7 +46,7 @@ pub fn parse_src_ipaddr_proto_dst_port( match ipv4_packet.get_next_level_protocol() { IpNextHeaderProtocols::Tcp => { - let ipv4_payload = ipv4_packet.payload(); + let ipv4_payload = libpcap_analyzer::extract_payload_l3_ipv4(ctx, &ipv4_packet)?; match TcpPacket::new(ipv4_payload) { Some(ref tcp) => { let dst_port = tcp.get_destination(); @@ -66,7 +70,10 @@ pub fn parse_src_ipaddr_proto_dst_port( } } -pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result { +pub fn parse_two_tuple_proto_ipid( + _ctx: &ParseContext, + payload: &[u8], +) -> Result { let ipv4_packet = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; let src_ipaddr = IpAddr::V4(ipv4_packet.get_source()); @@ -76,7 +83,7 @@ pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result Result { +pub fn parse_five_tuple(ctx: &ParseContext, payload: &[u8]) -> Result { let ipv4_packet = Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?; @@ -85,7 +92,7 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result { match ipv4_packet.get_next_level_protocol() { IpNextHeaderProtocols::Tcp => { - let ipv4_payload = ipv4_packet.payload(); + let ipv4_payload = libpcap_analyzer::extract_payload_l3_ipv4(ctx, &ipv4_packet)?; match TcpPacket::new(ipv4_payload) { Some(ref tcp) => { let src_port = tcp.get_source(); @@ -103,22 +110,25 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result { )), } } - IpNextHeaderProtocols::Udp => match UdpPacket::new(ipv4_packet.payload()) { - Some(ref udp) => { - let src_port = udp.get_source(); - let dst_port = udp.get_destination(); - Ok(FiveTuple { - src: src_ipaddr, - dst: dst_ipaddr, - proto: 17_u8, - src_port, - dst_port, - }) + IpNextHeaderProtocols::Udp => { + let ipv4_payload = libpcap_analyzer::extract_payload_l3_ipv4(ctx, &ipv4_packet)?; + match UdpPacket::new(ipv4_payload) { + Some(ref udp) => { + let src_port = udp.get_source(); + let dst_port = udp.get_destination(); + Ok(FiveTuple { + src: src_ipaddr, + dst: dst_ipaddr, + proto: 17_u8, + src_port, + dst_port, + }) + } + None => Err(Error::Pnet( + "Expected UDP packet in Ipv4 but could not parse", + )), } - None => Err(Error::Pnet( - "Expected UDP packet in Ipv4 but could not parse", - )), - }, + } _ => Ok(FiveTuple { src: src_ipaddr, dst: dst_ipaddr, @@ -130,10 +140,11 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result { } pub fn parse_two_tuple_proto_ipid_five_tuple( + ctx: &ParseContext, payload: &[u8], ) -> Result { - let two_tuple_proto_ipid = parse_two_tuple_proto_ipid(payload)?; - let five_tuple = parse_five_tuple(payload)?; + let two_tuple_proto_ipid = parse_two_tuple_proto_ipid(ctx, payload)?; + let five_tuple = parse_five_tuple(ctx, payload)?; let two_tuple_proto_ipid_five_tuple = TwoTupleProtoIpidFiveTuple::new(Some(two_tuple_proto_ipid), Some(five_tuple)); Ok(two_tuple_proto_ipid_five_tuple) diff --git a/pcap-rewrite/src/filters/key_parser_ipv6.rs b/pcap-rewrite/src/filters/key_parser_ipv6.rs index cce85f3..f134b3a 100644 --- a/pcap-rewrite/src/filters/key_parser_ipv6.rs +++ b/pcap-rewrite/src/filters/key_parser_ipv6.rs @@ -7,24 +7,27 @@ use pnet_packet::tcp::TcpPacket; use pnet_packet::udp::UdpPacket; use crate::filters::ipv6_utils; -use libpcap_tools::{Error, FiveTuple}; +use libpcap_tools::{Error, FiveTuple, ParseContext}; use super::fragmentation::two_tuple_proto_ipid::TwoTupleProtoIpid; use super::fragmentation::two_tuple_proto_ipid_five_tuple::TwoTupleProtoIpidFiveTuple; -pub fn parse_src_ipaddr(payload: &[u8]) -> Result { +pub fn parse_src_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result { let ipv6 = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; Ok(IpAddr::V6(ipv6.get_source())) } -pub fn parse_dst_ipaddr(payload: &[u8]) -> Result { +pub fn parse_dst_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result { let ipv6 = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; Ok(IpAddr::V6(ipv6.get_destination())) } -pub fn parse_src_dst_ipaddr(payload: &[u8]) -> Result<(IpAddr, IpAddr), Error> { +pub fn parse_src_dst_ipaddr( + _ctx: &ParseContext, + payload: &[u8], +) -> Result<(IpAddr, IpAddr), Error> { let ipv6_packet = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; let src_ipaddr = IpAddr::V6(ipv6_packet.get_source()); @@ -33,6 +36,7 @@ pub fn parse_src_dst_ipaddr(payload: &[u8]) -> Result<(IpAddr, IpAddr), Error> { } pub fn parse_src_ipaddr_proto_dst_port( + _ctx: &ParseContext, payload: &[u8], ) -> Result<(IpAddr, IpNextHeaderProtocol, u16), Error> { let ipv6_packet = @@ -66,7 +70,10 @@ pub fn parse_src_ipaddr_proto_dst_port( } } -pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result, Error> { +pub fn parse_two_tuple_proto_ipid( + _ctx: &ParseContext, + payload: &[u8], +) -> Result, Error> { let ipv6_packet = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; let src_ipaddr = IpAddr::V6(ipv6_packet.get_destination()); @@ -88,7 +95,7 @@ pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result Result { +pub fn parse_five_tuple(_ctx: &ParseContext, payload: &[u8]) -> Result { let ipv6_packet = Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?; @@ -142,11 +149,12 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result { } pub fn parse_two_tuple_proto_ipid_five_tuple( + ctx: &ParseContext, payload: &[u8], ) -> Result { Ok(TwoTupleProtoIpidFiveTuple::new( - parse_two_tuple_proto_ipid(payload)?, + parse_two_tuple_proto_ipid(ctx, payload)?, // TODO: replace by dedicated error type to distinguish between Ipv6Packet parsing error and TcpPacket/UdpPacket error related to fragmentation - parse_five_tuple(payload).ok(), + parse_five_tuple(ctx, payload).ok(), )) } diff --git a/pcap-rewrite/src/rewriter.rs b/pcap-rewrite/src/rewriter.rs index 67ba118..839d46d 100644 --- a/pcap-rewrite/src/rewriter.rs +++ b/pcap-rewrite/src/rewriter.rs @@ -144,7 +144,7 @@ impl PcapAnalyzer for Rewriter { if self.run_pre_analysis { // run pre-analysis plugins for p in self.filters.iter_mut() { - if let Err(e) = p.pre_analyze(packet) { + if let Err(e) = p.pre_analyze(ctx, packet) { error!("Pre-analysis plugin returned fatal error {}", e); return Err(Error::Generic("Pre-analysis fatal error")); }