Skip to content

Commit

Permalink
extend ctx use in parsing function and add ipv4 l3 payload parsing fu…
Browse files Browse the repository at this point in the history
…nction
  • Loading branch information
johanmazelanssi authored and chifflier committed Jul 31, 2024
1 parent e896973 commit 12385b1
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 55 deletions.
1 change: 1 addition & 0 deletions pcap-rewrite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
8 changes: 4 additions & 4 deletions pcap-rewrite/src/filters/dispatch_filter.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Key> = Box<dyn Fn(&[u8]) -> Result<Key, Error>>;
pub type GetKeyFn<Key> = Box<dyn Fn(&ParseContext, &[u8]) -> Result<Key, Error>>;
/// Function to keep/drop extract key from container
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, Error>>;

Expand Down Expand Up @@ -67,8 +67,8 @@ impl<Container, Key> DispatchFilter<Container, Key> {
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}",
Expand Down
4 changes: 2 additions & 2 deletions pcap-rewrite/src/filters/filter.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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(())
}

Expand Down
26 changes: 16 additions & 10 deletions pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use super::convert_fn;
/// Function to convert TwoTupleProtoIpid/FiveTuple data to key container
pub type ConvertFn<Container> = Box<dyn Fn(&HashSet<TwoTupleProtoIpidFiveTuple>) -> Container>;
/// Function to extract key from data
pub type GetKeyFn<Key> = Box<dyn Fn(&[u8]) -> Result<Key, Error>>;
pub type GetKeyFn<Key> = Box<dyn Fn(&ParseContext, &[u8]) -> Result<Key, Error>>;
/// Function to keep/drop extract key from container
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, Error>>;

Expand Down Expand Up @@ -64,7 +64,11 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}
}

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 {
Expand All @@ -74,6 +78,7 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}

filter_utils::extract_callback_ethernet(
ctx,
&fragmentation_test::is_ipv4_first_fragment,
&fragmentation_test::is_ipv6_first_fragment,
data,
Expand All @@ -82,8 +87,8 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
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}",
Expand All @@ -105,6 +110,7 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}

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,
Expand All @@ -114,10 +120,10 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
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!(
Expand Down Expand Up @@ -161,8 +167,8 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
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}",
Expand Down Expand Up @@ -198,8 +204,8 @@ impl<Container, Key> Filter for FragmentationFilter<Container, Key> {
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> {
Expand Down
12 changes: 7 additions & 5 deletions pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs
Original file line number Diff line number Diff line change
@@ -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<bool, Error> {
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<bool, Error> {
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();

Expand All @@ -15,8 +16,9 @@ pub fn is_ipv4_first_fragment(payload: &[u8]) -> Result<bool, Error> {
Ok(mf_flag == 1 && fragment_offset == 0)
}

pub fn is_ipv6_first_fragment(payload: &[u8]) -> Result<bool, Error> {
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<bool, Error> {
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 {
Expand Down
61 changes: 36 additions & 25 deletions pcap-rewrite/src/filters/key_parser_ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IpAddr, Error> {
pub fn parse_src_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result<IpAddr, Error> {
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<IpAddr, Error> {
pub fn parse_dst_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result<IpAddr, Error> {
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());
Expand All @@ -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 =
Expand All @@ -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();
Expand All @@ -66,7 +70,10 @@ pub fn parse_src_ipaddr_proto_dst_port(
}
}

pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result<TwoTupleProtoIpid, Error> {
pub fn parse_two_tuple_proto_ipid(
_ctx: &ParseContext,
payload: &[u8],
) -> Result<TwoTupleProtoIpid, 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());
Expand All @@ -76,7 +83,7 @@ pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result<TwoTupleProtoIpid, E
Ok(TwoTupleProtoIpid::new(src_ipaddr, dst_ipaddr, proto, ip_id))
}

pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {
pub fn parse_five_tuple(ctx: &ParseContext, payload: &[u8]) -> Result<FiveTuple, Error> {
let ipv4_packet =
Ipv4Packet::new(payload).ok_or(Error::Pnet("Expected Ipv4 packet but could not parse"))?;

Expand All @@ -85,7 +92,7 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {

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();
Expand All @@ -103,22 +110,25 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {
)),
}
}
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,
Expand All @@ -130,10 +140,11 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {
}

pub fn parse_two_tuple_proto_ipid_five_tuple(
ctx: &ParseContext,
payload: &[u8],
) -> Result<TwoTupleProtoIpidFiveTuple, Error> {
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)
Expand Down
24 changes: 16 additions & 8 deletions pcap-rewrite/src/filters/key_parser_ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IpAddr, Error> {
pub fn parse_src_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result<IpAddr, Error> {
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<IpAddr, Error> {
pub fn parse_dst_ipaddr(_ctx: &ParseContext, payload: &[u8]) -> Result<IpAddr, Error> {
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());
Expand All @@ -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 =
Expand Down Expand Up @@ -66,7 +70,10 @@ pub fn parse_src_ipaddr_proto_dst_port(
}
}

pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result<Option<TwoTupleProtoIpid>, Error> {
pub fn parse_two_tuple_proto_ipid(
_ctx: &ParseContext,
payload: &[u8],
) -> Result<Option<TwoTupleProtoIpid>, 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());
Expand All @@ -88,7 +95,7 @@ pub fn parse_two_tuple_proto_ipid(payload: &[u8]) -> Result<Option<TwoTupleProto
}
}

pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {
pub fn parse_five_tuple(_ctx: &ParseContext, payload: &[u8]) -> Result<FiveTuple, Error> {
let ipv6_packet =
Ipv6Packet::new(payload).ok_or(Error::Pnet("Expected Ipv6 packet but could not parse"))?;

Expand Down Expand Up @@ -142,11 +149,12 @@ pub fn parse_five_tuple(payload: &[u8]) -> Result<FiveTuple, Error> {
}

pub fn parse_two_tuple_proto_ipid_five_tuple(
ctx: &ParseContext,
payload: &[u8],
) -> Result<TwoTupleProtoIpidFiveTuple, Error> {
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(),
))
}
2 changes: 1 addition & 1 deletion pcap-rewrite/src/rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
Expand Down

0 comments on commit 12385b1

Please sign in to comment.