Skip to content

Commit

Permalink
change error in pcap-rewrite filters from String to libpcap_tools::Error
Browse files Browse the repository at this point in the history
  • Loading branch information
johanmazelanssi committed Jul 29, 2024
1 parent 74a8985 commit 72b7c5e
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 101 deletions.
16 changes: 10 additions & 6 deletions pcap-rewrite/src/filters/common_filters.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::filters::filter::*;
use libpcap_tools::ParseContext;
use libpcap_tools::{Error,ParseContext};
use pcap_parser::data::{PacketData, ETHERTYPE_IPV4, ETHERTYPE_IPV6};
use pnet_packet::ethernet::EthernetPacket;
use pnet_packet::ipv4::Ipv4Packet;
Expand All @@ -18,7 +18,7 @@ pub struct IPFilter {
}

impl Filter for IPFilter {
fn filter<'i>(&self, _ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, String> {
fn filter<'i>(&self, _ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, Error> {
match i {
PacketData::L2(data) => {
let p = match EthernetPacket::new(data) {
Expand Down Expand Up @@ -56,7 +56,9 @@ impl Filter for IPFilter {
}
}
PacketData::L4(_, _) => Err("Cannot filter IP, L4 content")?,
PacketData::Unsupported(_) => Err("Cannot filter IP, unsupported data".to_owned()),
PacketData::Unsupported(_) => {
Err(Error::Unsupported("Cannot filter IP, unsupported data"))
}
}
}
}
Expand Down Expand Up @@ -104,7 +106,7 @@ pub struct SourceFilter {
}

impl Filter for SourceFilter {
fn filter<'i>(&self, _ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, String> {
fn filter<'i>(&self, _ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, Error> {
match i {
PacketData::L2(data) => {
let p = match EthernetPacket::new(data) {
Expand All @@ -124,8 +126,10 @@ impl Filter for SourceFilter {
Ok(Verdict::Drop)
}
}
PacketData::L4(_, _) => Err("Cannot filter source, L4 content".to_string()),
PacketData::Unsupported(_) => Err("Cannot filter source, unsupported data".to_string()),
PacketData::L4(_, _) => Err(Error::Unsupported("Cannot filter source, L4 content")),
PacketData::Unsupported(_) => {
Err(Error::Unsupported("Cannot filter source, unsupported data"))
}
}
}
}
Expand Down
26 changes: 15 additions & 11 deletions pcap-rewrite/src/filters/dispatch_filter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::io;
use std::net::IpAddr;
use std::path::Path;
use log::warn;

use libpcap_tools::{FiveTuple, ParseContext};
use libpcap_tools::{Error, FiveTuple, ParseContext};
use pcap_parser::data::PacketData;
use pnet_packet::ethernet::{EtherType, EtherTypes};
use pnet_packet::ip::IpNextHeaderProtocol;
Expand All @@ -19,9 +20,9 @@ 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, String>>;
pub type GetKeyFn<Key> = Box<dyn Fn(&[u8]) -> Result<Key, Error>>;
/// Function to keep/drop extract key from container
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, String>>;
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, Error>>;

pub struct DispatchFilter<Container, Key> {
key_container: Container,
Expand Down Expand Up @@ -49,11 +50,11 @@ impl<Container, Key> DispatchFilter<Container, Key> {
&self,
_ctx: &ParseContext,
packet_data: PacketData<'j>,
) -> FResult<PacketData<'j>, String> {
) -> FResult<PacketData<'j>, Error> {
let key = match packet_data {
PacketData::L2(data) => {
if data.len() < 14 {
return Err("L2 data too small for ethernet".to_owned());
return Err(Error::DataParser("L2 data too small for ethernet"));
}

filter_utils::extract_callback_ethernet(
Expand All @@ -67,11 +68,14 @@ impl<Container, Key> DispatchFilter<Container, Key> {
match ether_type {
EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(data)?,
EtherTypes::Ipv6 => (self.get_key_from_ipv4_l3_data)(data)?,
_ => Err(format!(
"Unimplemented Ethertype in L3 {:?}/{:x}",
ether_type,
ether_type.to_primitive_values().0
))?,
_ => {
warn!(
"Unimplemented Ethertype in L3 {:?}/{:x}",
ether_type,
ether_type.to_primitive_values().0
);
Err(Error::Unimplemented("Unimplemented Ethertype in L3"))?
}
}
}
PacketData::L4(_, _) => unimplemented!(),
Expand All @@ -92,7 +96,7 @@ impl<Container, Key> DispatchFilter<Container, Key> {
}

impl<Container, Key> Filter for DispatchFilter<Container, Key> {
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, String> {
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, Error> {
self.keep(ctx, i)
}
}
Expand Down
10 changes: 5 additions & 5 deletions pcap-rewrite/src/filters/filter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use libpcap_tools::{Packet,ParseContext};
use libpcap_tools::{Error,Packet,ParseContext};
use pcap_parser::data::PacketData;

/// Verdict emitted by a Filter
Expand All @@ -13,7 +13,7 @@ pub enum Verdict<O> {
pub type FResult<O, E> = Result<Verdict<O>, E>;

pub trait Filter {
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, String>;
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, Error>;

/// Does this filter plugin require a first pass to pre-analyze data? (default: `false`)
fn require_pre_analysis(&self) -> bool {
Expand All @@ -25,11 +25,11 @@ 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<(), String> {
fn pre_analyze(&mut self, _packet: &Packet) -> Result<(), Error> {
Ok(())
}

fn preanalysis_done(&mut self) -> Result<(), String> {
fn preanalysis_done(&mut self) -> Result<(), Error> {
Ok(())
}
}
Expand All @@ -38,7 +38,7 @@ pub fn apply_filters<'d>(
filters: &'d [Box<dyn Filter>],
ctx: &ParseContext,
data: PacketData<'d>,
) -> FResult<PacketData<'d>, String> {
) -> FResult<PacketData<'d>, Error> {
filters.iter().try_fold(Verdict::Accept(data), |d, f| {
if let Verdict::Accept(data) = d {
f.filter(ctx, data)
Expand Down
43 changes: 27 additions & 16 deletions pcap-rewrite/src/filters/filter_utils.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
use log::warn;

use libpcap_tools::Error;
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, String>,
get_key_from_ipv6_l3_data: &dyn Fn(&[u8]) -> Result<D, String>,
get_key_from_ipv4_l3_data: &dyn Fn(&[u8]) -> Result<D, Error>,
get_key_from_ipv6_l3_data: &dyn Fn(&[u8]) -> Result<D, Error>,
packet_data: &[u8],
) -> Result<D, String> {
let ethernet_packet =
EthernetPacket::new(packet_data).ok_or("Expected Ethernet packet but not found")?;
) -> 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::Vlan => {
// 802.11q
let vlan_packet = VlanPacket::new(ethernet_packet.payload())
.ok_or("Expected VLAN packet but not found")?;
.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()),
_ => Err(format!(
"Unimplemented Ethertype in 33024/802.11q: {:?}/{:x}",
vlan_packet.get_ethertype(),
vlan_packet.get_ethertype().to_primitive_values().0
)),
_ => {
warn!(
"Unimplemented Ethertype in 33024/802.11q: {:?}/{:x}",
vlan_packet.get_ethertype(),
vlan_packet.get_ethertype().to_primitive_values().0
);
Err(Error::Unimplemented(
"Unimplemented Ethertype in 33024/802.11q",
))
}
}
}
_ => Err(format!(
"Unimplemented Ethertype: {:?}/{:x}",
ethernet_packet.get_ethertype(),
ethernet_packet.get_ethertype().to_primitive_values().0
)),
_ => {
warn!(
"Unimplemented Ethertype: {:?}/{:x}",
ethernet_packet.get_ethertype(),
ethernet_packet.get_ethertype().to_primitive_values().0
);
Err(Error::Unimplemented("Unimplemented Ethertype"))
}
}
}
51 changes: 31 additions & 20 deletions pcap-rewrite/src/filters/fragmentation/fragmentation_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::collections::HashSet;
use std::io;
use std::net::IpAddr;

use log::warn;
use pcap_parser::data::PacketData;
use pnet_packet::ethernet::{EtherType, EtherTypes};
use pnet_packet::ip::IpNextHeaderProtocol;

use libpcap_tools::{Packet, ParseContext};
use libpcap_tools::{Error, Packet, ParseContext};

use crate::container::five_tuple_container::FiveTupleC;
use crate::container::ipaddr_container::IpAddrC;
Expand All @@ -28,9 +29,9 @@ 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, String>>;
pub type GetKeyFn<Key> = Box<dyn Fn(&[u8]) -> Result<Key, Error>>;
/// Function to keep/drop extract key from container
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, String>>;
pub type KeepFn<Container, Key> = Box<dyn Fn(&Container, &Key) -> Result<bool, Error>>;

pub struct FragmentationFilter<Container, Key> {
data_hs: HashSet<TwoTupleProtoIpidFiveTuple>,
Expand Down Expand Up @@ -63,13 +64,13 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}
}

fn test_fragmentation_and_save(&mut self, packet: &Packet) -> Result<(), String> {
fn test_fragmentation_and_save(&mut self, 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 {
PacketData::L2(data) => {
if data.len() < 14 {
return Err("L2 data too small for ethernet".to_string());
return Err(Error::DataParser("L2 data too small for ethernet"));
}

filter_utils::extract_callback_ethernet(
Expand All @@ -83,7 +84,13 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
match ether_type {
EtherTypes::Ipv4 => (fragmentation_test::is_ipv4_first_fragment)(data)?,
EtherTypes::Ipv6 => (fragmentation_test::is_ipv6_first_fragment)(data)?,
_ => return Err(format!("{} is not implmented", ether_type)),
_ => {
warn!(
"Unimplemented Ethertype in L3: {:?}/{:x}",
ether_type, ether_type.0
);
return Err(Error::Unimplemented("Unimplemented EtherType in L3"));
}
}
}
PacketData::L4(_, _) => unimplemented!(),
Expand All @@ -94,7 +101,7 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
let data_option: Option<TwoTupleProtoIpidFiveTuple> = match packet.data {
PacketData::L2(data) => {
if data.len() < 14 {
return Err("L2 data too small for ethernet".to_string());
return Err(Error::DataParser("L2 data too small for ethernet"));
}

Some(filter_utils::extract_callback_ethernet(
Expand All @@ -113,10 +120,11 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
(key_parser_ipv6::parse_two_tuple_proto_ipid_five_tuple)(data)?,
),
_ => {
return Err(format!(
"Unimplemented Ethertype in L3 {:?}/{:x}",
warn!(
"Unimplemented Ethertype in L3: {:?}/{:x}",
ether_type, ether_type.0
))
);
return Err(Error::Unimplemented("Unimplemented Ethertype in L3"));
}
}
}
Expand All @@ -136,11 +144,11 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
&self,
_ctx: &ParseContext,
packet_data: PacketData<'j>,
) -> FResult<PacketData<'j>, String> {
) -> FResult<PacketData<'j>, Error> {
let key = match packet_data {
PacketData::L2(data) => {
if data.len() < 14 {
return Err("L2 data too small for ethernet".to_owned());
return Err(Error::DataParser("L2 data too small for ethernet"));
}

filter_utils::extract_callback_ethernet(
Expand All @@ -154,10 +162,13 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
match ether_type {
EtherTypes::Ipv4 => (self.get_key_from_ipv4_l3_data)(data)?,
EtherTypes::Ipv6 => (self.get_key_from_ipv6_l3_data)(data)?,
_ => Err(format!(
"Unimplemented Ethertype in L3 {:?}/{:x}",
ether_type, ether_type.0
))?,
_ => {
warn!(
"Unimplemented Ethertype in L3: {:?}/{:x}",
ether_type, ether_type.0
);
return Err(Error::Unimplemented("Unimplemented Ethertype in L3"));
}
}
}
PacketData::L4(_, _) => unimplemented!(),
Expand All @@ -178,19 +189,19 @@ impl<Container, Key> FragmentationFilter<Container, Key> {
}

impl<Container, Key> Filter for FragmentationFilter<Container, Key> {
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, String> {
fn filter<'i>(&self, ctx: &ParseContext, i: PacketData<'i>) -> FResult<PacketData<'i>, Error> {
self.keep(ctx, i)
}

fn require_pre_analysis(&self) -> bool {
true
}

fn pre_analyze(&mut self, _packet: &Packet) -> Result<(), String> {
fn pre_analyze(&mut self, _packet: &Packet) -> Result<(), Error> {
self.test_fragmentation_and_save(_packet)
}

fn preanalysis_done(&mut self) -> Result<(), String> {
fn preanalysis_done(&mut self) -> Result<(), Error> {
self.key_container = (self.convert_data_hs_c)(&self.data_hs);
Ok(())
}
Expand All @@ -199,7 +210,7 @@ impl<Container, Key> Filter for FragmentationFilter<Container, Key> {
pub fn test_two_tuple_proto_ipid_five_tuple_option_in_container(
container_tuple: &(TwoTupleProtoIpidC, FiveTupleC),
two_tuple_proto_ipid_five_tuple: &TwoTupleProtoIpidFiveTuple,
) -> Result<bool, String> {
) -> Result<bool, Error> {
let (two_tuple_proto_ipid_c, five_tuple_c) = container_tuple;

let in_0 = match two_tuple_proto_ipid_five_tuple.get_two_tuple_proto_ipid_option() {
Expand Down
10 changes: 6 additions & 4 deletions pcap-rewrite/src/filters/fragmentation/fragmentation_test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use libpcap_tools::Error;

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, String> {
let ipv4_packet = Ipv4Packet::new(payload).ok_or("Expected Ipv4 packet but not found")?;
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"))?;
let flags = ipv4_packet.get_flags();
let fragment_offset = ipv4_packet.get_fragment_offset();

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

pub fn is_ipv6_first_fragment(payload: &[u8]) -> Result<bool, String> {
let ipv6_packet = Ipv6Packet::new(payload).ok_or("Expected Ipv6 packet but not found")?;
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"))?;
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

0 comments on commit 72b7c5e

Please sign in to comment.