PcapFileEx.PreFilter (pcap_file_ex v0.5.5)

View Source

BPF-style pre-filtering in the Rust layer for high-performance packet filtering.

This module provides filters that are applied in the Rust layer before packets are deserialized to Elixir terms, resulting in 10-100x performance improvements for selective filtering on large files.

Important Notes

File Format Auto-Detection: If you get an error like "Invalid field value: PcapHeader: wrong magic number", your file might be PCAPNG format despite having a .pcap extension. Use PcapFileEx.open/1 for auto-detection instead of Pcap.open/1 or PcapNg.open/1.

# ✓ RECOMMENDED: Auto-detect format
{:ok, reader} = PcapFileEx.open("capture.pcap")

# ✗ AVOID: Manual format selection (unless you're sure)
{:ok, reader} = PcapFileEx.Pcap.open("capture.pcap")  # Fails if actually PCAPNG

Filter Types

IP Filters

Port Filters

Protocol Filters

  • protocol/1 - Match protocol (tcp, udp, icmp, ipv4, ipv6, etc.)

Size Filters

Timestamp Filters

Logical Operators

Examples

# Filter TCP packets on port 80
filters = [
  PcapFileEx.PreFilter.protocol("tcp"),
  PcapFileEx.PreFilter.port_dest(80)
]

{:ok, reader} = PcapFileEx.Pcap.open("capture.pcap")
:ok = PcapFileEx.Pcap.set_filter(reader, filters)

# Now reading will only return matching packets
PcapFileEx.Stream.from_reader(reader)
|> Enum.take(10)

# Filter by IP range
filters = [
  PcapFileEx.PreFilter.ip_source_cidr("192.168.1.0/24"),
  PcapFileEx.PreFilter.size_range(100, 1500)
]

# Combine filters with logical operators
filters = [
  PcapFileEx.PreFilter.any([
    PcapFileEx.PreFilter.port_dest(80),
    PcapFileEx.PreFilter.port_dest(443)
  ])
]

Performance

Pre-filters are significantly faster than post-processing filters because:

  • Packets are filtered in Rust before creating Elixir terms
  • No memory allocation for filtered-out packets
  • Reduced garbage collection pressure
  • Lower CPU usage (no unnecessary protocol parsing)

For large files with selective filtering (e.g., "show me 10 packets on port 80 from a 10GB capture"), pre-filters can be 10-100x faster than post-processing.

Summary

Functions

All filters must match (AND).

Any filter can match (OR).

Match destination IP address.

Match destination IP in CIDR range.

Match source IP address.

Match source IP in CIDR range.

Invert filter (NOT).

Match destination port.

Match destination port in range.

Match source port.

Match source port in range.

Match protocol.

Match maximum packet size (original length).

Match minimum packet size (original length).

Match packet size in range (original length).

Match packets before timestamp (Unix seconds).

Match packets after timestamp (Unix seconds).

Types

filter()

@type filter() ::
  {:ip_source, String.t()}
  | {:ip_dest, String.t()}
  | {:ip_source_cidr, String.t()}
  | {:ip_dest_cidr, String.t()}
  | {:port_source, 0..65535}
  | {:port_dest, 0..65535}
  | {:port_source_range, 0..65535, 0..65535}
  | {:port_dest_range, 0..65535, 0..65535}
  | {:protocol, String.t()}
  | {:size_min, non_neg_integer()}
  | {:size_max, non_neg_integer()}
  | {:size_range, non_neg_integer(), non_neg_integer()}
  | {:timestamp_min, non_neg_integer()}
  | {:timestamp_max, non_neg_integer()}
  | {:and, [filter()]}
  | {:or, [filter()]}
  | {:not, filter()}

Functions

all(filters)

@spec all([filter()]) :: filter()

All filters must match (AND).

Examples

PreFilter.all([
  PreFilter.protocol("tcp"),
  PreFilter.port_dest(80)
])

any(filters)

@spec any([filter()]) :: filter()

Any filter can match (OR).

Examples

PreFilter.any([
  PreFilter.port_dest(80),
  PreFilter.port_dest(443)
])

ip_dest(ip)

@spec ip_dest(String.t()) :: filter()

Match destination IP address.

Examples

PreFilter.ip_dest("8.8.8.8")

ip_dest_cidr(cidr)

@spec ip_dest_cidr(String.t()) :: filter()

Match destination IP in CIDR range.

Examples

PreFilter.ip_dest_cidr("10.0.0.0/8")

ip_source(ip)

@spec ip_source(String.t()) :: filter()

Match source IP address.

Examples

PreFilter.ip_source("192.168.1.1")

ip_source_cidr(cidr)

@spec ip_source_cidr(String.t()) :: filter()

Match source IP in CIDR range.

Examples

PreFilter.ip_source_cidr("192.168.1.0/24")
PreFilter.ip_source_cidr("2001:db8::/32")

not_filter(filter)

@spec not_filter(filter()) :: filter()

Invert filter (NOT).

Examples

PreFilter.not_filter(PreFilter.protocol("tcp"))

port_dest(port)

@spec port_dest(0..65535) :: filter()

Match destination port.

Examples

PreFilter.port_dest(80)
PreFilter.port_dest(443)

port_dest_range(min, max)

@spec port_dest_range(0..65535, 0..65535) :: filter()

Match destination port in range.

Examples

PreFilter.port_dest_range(1024, 65535)

port_source(port)

@spec port_source(0..65535) :: filter()

Match source port.

Examples

PreFilter.port_source(8080)

port_source_range(min, max)

@spec port_source_range(0..65535, 0..65535) :: filter()

Match source port in range.

Examples

PreFilter.port_source_range(8000, 9000)

protocol(proto)

@spec protocol(String.t()) :: filter()

Match protocol.

Supported protocols: tcp, udp, icmp, icmpv6, ipv4, ipv6

Examples

PreFilter.protocol("tcp")
PreFilter.protocol("udp")
PreFilter.protocol("icmp")

size_max(bytes)

@spec size_max(non_neg_integer()) :: filter()

Match maximum packet size (original length).

Examples

PreFilter.size_max(1500)

size_min(bytes)

@spec size_min(non_neg_integer()) :: filter()

Match minimum packet size (original length).

Examples

PreFilter.size_min(100)

size_range(min, max)

@spec size_range(non_neg_integer(), non_neg_integer()) :: filter()

Match packet size in range (original length).

Examples

PreFilter.size_range(100, 1500)

timestamp_max(secs)

@spec timestamp_max(non_neg_integer()) :: filter()

Match packets before timestamp (Unix seconds).

Examples

PreFilter.timestamp_max(1730818800)

timestamp_min(secs)

@spec timestamp_min(non_neg_integer()) :: filter()

Match packets after timestamp (Unix seconds).

Examples

PreFilter.timestamp_min(1730732400)