Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pcap-rewrite: Add l3 payload parsing function #29

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
36 changes: 19 additions & 17 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,8 @@ 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>>;
pub type GetKeyFn<Key> = 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 @@ -48,7 +49,7 @@ impl<Container, Key> DispatchFilter<Container, Key> {

pub fn keep<'j>(
&self,
_ctx: &ParseContext,
ctx: &ParseContext,
packet_data: PacketData<'j>,
) -> FResult<PacketData<'j>, Error> {
let key = match packet_data {
Expand All @@ -58,16 +59,17 @@ impl<Container, Key> DispatchFilter<Container, Key> {
}

filter_utils::extract_callback_ethernet(
&self.get_key_from_ipv4_l3_data,
&self.get_key_from_ipv6_l3_data,
ctx,
self.get_key_from_ipv4_l3_data,
self.get_key_from_ipv6_l3_data,
data,
)?
}
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 Expand Up @@ -123,8 +125,8 @@ impl DispatchFilterBuilder {

Ok(Box::new(DispatchFilter::new(
ipaddr_container,
Box::new(key_parser_ipv4::parse_src_ipaddr),
Box::new(key_parser_ipv6::parse_src_ipaddr),
key_parser_ipv4::parse_src_ipaddr,
key_parser_ipv6::parse_src_ipaddr,
Box::new(keep),
)))
}
Expand All @@ -141,8 +143,8 @@ impl DispatchFilterBuilder {

Ok(Box::new(DispatchFilter::new(
ipaddr_container,
Box::new(key_parser_ipv4::parse_dst_ipaddr),
Box::new(key_parser_ipv6::parse_dst_ipaddr),
key_parser_ipv4::parse_dst_ipaddr,
key_parser_ipv6::parse_dst_ipaddr,
keep,
)))
}
Expand All @@ -161,8 +163,8 @@ impl DispatchFilterBuilder {

Ok(Box::new(DispatchFilter::new(
ipaddr_container,
Box::new(key_parser_ipv4::parse_src_dst_ipaddr),
Box::new(key_parser_ipv6::parse_src_dst_ipaddr),
key_parser_ipv4::parse_src_dst_ipaddr,
key_parser_ipv6::parse_src_dst_ipaddr,
keep,
)))
}
Expand All @@ -183,8 +185,8 @@ impl DispatchFilterBuilder {

Ok(Box::new(DispatchFilter::new(
ipaddr_proto_port_container,
Box::new(key_parser_ipv4::parse_src_ipaddr_proto_dst_port),
Box::new(key_parser_ipv6::parse_src_ipaddr_proto_dst_port),
key_parser_ipv4::parse_src_ipaddr_proto_dst_port,
key_parser_ipv6::parse_src_ipaddr_proto_dst_port,
keep,
)))
}
Expand All @@ -199,8 +201,8 @@ impl DispatchFilterBuilder {

Ok(Box::new(DispatchFilter::new(
five_tuple_container,
Box::new(key_parser_ipv4::parse_five_tuple),
Box::new(key_parser_ipv6::parse_five_tuple),
key_parser_ipv4::parse_five_tuple,
key_parser_ipv6::parse_five_tuple,
keep,
)))
}
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
24 changes: 15 additions & 9 deletions pcap-rewrite/src/filters/filter_utils.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
use log::warn;

use libpcap_tools::Error;
use libpcap_tools::{Error, ParseContext};
use pnet_packet::ethernet::{EtherTypes, EthernetPacket};
use pnet_packet::vlan::VlanPacket;
use pnet_packet::Packet;
use pnet_packet::PrimitiveValues;

pub fn extract_callback_ethernet<D>(
get_key_from_ipv4_l3_data: &dyn Fn(&[u8]) -> Result<D, Error>,
get_key_from_ipv6_l3_data: &dyn Fn(&[u8]) -> Result<D, Error>,
// TODO: find simpler solution for function parameter with genericity
chifflier marked this conversation as resolved.
Show resolved Hide resolved
pub fn extract_callback_ethernet<D, F1, F2>(
ctx: &ParseContext,
get_key_from_ipv4_l3_data: F1,
get_key_from_ipv6_l3_data: F2,
packet_data: &[u8],
) -> Result<D, Error> {
) -> Result<D, Error>
where
F1: Fn(&ParseContext, &[u8]) -> Result<D, Error>,
F2: Fn(&ParseContext, &[u8]) -> Result<D, Error>,
{
let ethernet_packet = EthernetPacket::new(packet_data)
.ok_or(Error::Pnet("Expected Ethernet packet but could not parse"))?;
match ethernet_packet.get_ethertype() {
EtherTypes::Ipv4 => (get_key_from_ipv4_l3_data)(ethernet_packet.payload()),
EtherTypes::Ipv6 => (get_key_from_ipv6_l3_data)(ethernet_packet.payload()),
EtherTypes::Ipv4 => (get_key_from_ipv4_l3_data)(ctx, ethernet_packet.payload()),
EtherTypes::Ipv6 => (get_key_from_ipv6_l3_data)(ctx, ethernet_packet.payload()),
EtherTypes::Vlan => {
// 802.11q
let vlan_packet = VlanPacket::new(ethernet_packet.payload())
.ok_or(Error::Pnet("Expected VLAN packet but could not parse"))?;
match vlan_packet.get_ethertype() {
EtherTypes::Ipv4 => (get_key_from_ipv4_l3_data)(ethernet_packet.payload()),
EtherTypes::Ipv6 => (get_key_from_ipv6_l3_data)(ethernet_packet.payload()),
EtherTypes::Ipv4 => (get_key_from_ipv4_l3_data)(ctx, ethernet_packet.payload()),
EtherTypes::Ipv6 => (get_key_from_ipv6_l3_data)(ctx, ethernet_packet.payload()),
_ => {
warn!(
"Unimplemented Ethertype in 33024/802.11q: {:?}/{:x}",
Expand Down
63 changes: 35 additions & 28 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> = 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,16 +78,17 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}

filter_utils::extract_callback_ethernet(
&fragmentation_test::is_ipv4_first_fragment,
&fragmentation_test::is_ipv6_first_fragment,
ctx,
fragmentation_test::is_ipv4_first_fragment,
fragmentation_test::is_ipv6_first_fragment,
data,
)?
}
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,19 +110,20 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}

Some(filter_utils::extract_callback_ethernet(
&key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple,
&key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple,
ctx,
key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple,
key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple,
data,
)?)
}
PacketData::L3(l3_layer_value_u8, data) => {
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 All @@ -142,26 +148,27 @@ impl<Container, Key> FragmentationFilter<Container, Key> {

pub fn keep<'j>(
&self,
_ctx: &ParseContext,
ctx: &ParseContext,
packet_data: PacketData<'j>,
) -> FResult<PacketData<'j>, Error> {
let key = match packet_data {
let key: Key = match packet_data {
PacketData::L2(data) => {
if data.len() < 14 {
return Err(Error::DataParser("L2 data too small for ethernet"));
}

filter_utils::extract_callback_ethernet(
&self.get_key_from_ipv4_l3_data,
&self.get_key_from_ipv6_l3_data,
ctx,
self.get_key_from_ipv4_l3_data,
self.get_key_from_ipv6_l3_data,
data,
)?
}
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 @@ -197,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 Expand Up @@ -248,8 +255,8 @@ impl FragmentationFilterBuilder {
HashSet::new(),
Box::new(convert_fn::convert_data_hs_to_src_ipaddrc),
ipaddr_container,
Box::new(key_parser_ipv4::parse_src_ipaddr),
Box::new(key_parser_ipv6::parse_src_ipaddr),
key_parser_ipv4::parse_src_ipaddr,
key_parser_ipv6::parse_src_ipaddr,
keep,
)))
}
Expand All @@ -267,8 +274,8 @@ impl FragmentationFilterBuilder {
HashSet::new(),
Box::new(convert_fn::convert_data_hs_to_dst_ipaddrc),
ipaddr_container,
Box::new(key_parser_ipv4::parse_dst_ipaddr),
Box::new(key_parser_ipv6::parse_dst_ipaddr),
key_parser_ipv4::parse_dst_ipaddr,
key_parser_ipv6::parse_dst_ipaddr,
keep,
)))
}
Expand All @@ -288,8 +295,8 @@ impl FragmentationFilterBuilder {
HashSet::new(),
Box::new(convert_fn::convert_data_hs_to_src_dst_ipaddrc),
ipaddr_container,
Box::new(key_parser_ipv4::parse_src_dst_ipaddr),
Box::new(key_parser_ipv6::parse_src_dst_ipaddr),
key_parser_ipv4::parse_src_dst_ipaddr,
key_parser_ipv6::parse_src_dst_ipaddr,
keep,
)))
}
Expand All @@ -310,8 +317,8 @@ impl FragmentationFilterBuilder {
HashSet::new(),
Box::new(convert_fn::convert_data_hs_to_src_ipaddr_proto_dst_port_container),
ipaddr_proto_port_container,
Box::new(key_parser_ipv4::parse_src_ipaddr_proto_dst_port),
Box::new(key_parser_ipv6::parse_src_ipaddr_proto_dst_port),
key_parser_ipv4::parse_src_ipaddr_proto_dst_port,
key_parser_ipv6::parse_src_ipaddr_proto_dst_port,
keep,
)))
}
Expand Down Expand Up @@ -340,8 +347,8 @@ impl FragmentationFilterBuilder {
HashSet::new(),
Box::new(convert_fn::convert_data_hs_to_ctuple),
(two_tuple_proto_proto_ipid_c, five_tuple_container),
Box::new(key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple),
Box::new(key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple),
key_parser_ipv4::parse_two_tuple_proto_ipid_five_tuple,
key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple,
keep,
)))
}
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
Loading
Loading