Skip to main content
Retis offers two distinct methods for filtering packets, both of which can operate simultaneously:
  • Packet-based filtering - Filters packets based on their content (headers)
  • Metadata-based filtering - Filters packets based on their associated kernel metadata
These filtering mechanisms ensure that only relevant packets are reported, reducing the volume of uninteresting events and improving the efficiency of packet tracing.
Filtering also works with packet tracking. Even if a packet doesn’t match the filter at a particular probe point, it will still be reported if it was previously tracked.

Packet Filtering

Packet filtering uses the familiar pcap-filter syntax. The filter expression is compiled to cBPF, then translated to eBPF for efficient in-kernel execution.

Basic Syntax

See man pcap-filter for complete syntax details. Here are common examples:
# Filter TCP traffic on port 443
$ retis collect -f 'tcp port 443'

# Filter ARP or TCP port 443
$ retis collect -f 'arp or tcp port 443'
L2+L3 packet filter(s) loaded

# Filter specific IP address
$ retis collect -f 'host 192.168.1.1'

# Filter UDP DNS traffic
$ retis collect -f 'udp port 53'
Retis automatically detects and generates both L2 and L3 filters based on your expression. This allows matching:
  • Fully formed packets with valid L2 headers (Layer 2)
  • Packets without L2 headers but with valid network headers (Layer 3)
This approach lets you match locally generated packets (which lack L2 headers initially) while still allowing matches based on L2 criteria like MAC addresses.
$ retis collect -f 'arp or tcp port 443'
L2+L3 packet filter(s) loaded
Internally, Retis generates two filters:
  • For probes where only network_header is valid: matches TCP port 443
  • For probes with valid mac_header: matches both ARP and TCP port 443

Metadata Filtering

Metadata filtering allows you to write filters that match packets based on their kernel metadata - any field in the sk_buff structure or nested data structures it references.

Syntax Overview

Metadata filters automatically follow struct pointers, allowing indirect access to structures referenced by sk_buff fields:
# Filter by network namespace
$ retis collect -m 'sk_buff.dev.nd_net.net.ns.inum == 4026531840'

# Filter by device name
$ retis collect -m 'sk_buff.dev.name == "eth0"'

# Filter by packet type and mark
$ retis collect -m 'sk_buff.pkt_type == 0x0 || sk_buff.mark == 0x100'
The sk_buff keyword must always be present and must always appear first in each expression.

Relational Operators

Metadata filters support standard comparison operators:
OperatorMeaningExample
==Equal tosk_buff.mark == 0x100
!=Not equal tosk_buff.mark != 0
<Less thansk_buff.len < 1500
<=Less than or equal tosk_buff.len <= 1500
>Greater thansk_buff.len > 64
>=Greater than or equal tosk_buff.len >= 64
(omit)Not equal to zero (implicit)sk_buff.cloned

Boolean Operators

Combine conditions with logical operators:
OperatorAlternateMeaningExample
&&andConjunctionsk_buff.mark == 0x100 && sk_buff.cloned
``orDisjunction`sk_buff.pkt_type == 0x0sk_buff.mark == 0x100`
Boolean operators have the same precedence and are left-associative by default. Use parentheses to change associativity:
# Left-associative (default)
$ retis collect -m 'sk_buff.pkt_type == 0x0 || sk_buff.mark == 0x100 && sk_buff.cloned'
This is equivalent to:
$ retis collect -m '(sk_buff.pkt_type == 0x0 || sk_buff.mark == 0x100) && sk_buff.cloned'
To change the evaluation order:
$ retis collect -m 'sk_buff.pkt_type == 0x0 || (sk_buff.mark == 0x100 && sk_buff.cloned)'
There’s no limit to parentheses nesting. Use them to avoid ambiguity and optimize short-circuit evaluation.

Numeric Values

Numeric values can be expressed in multiple bases:
# Decimal (base 10)
sk_buff.mark == 16

# Hexadecimal (base 16)
sk_buff.mark == 0x10

# Binary (base 2)
sk_buff.mark == 0b10000
All three examples above are equivalent.
Apply bitwise AND operations before comparison using masks:
# Extract and compare specific bits
$ retis collect -m 'sk_buff._nfct:0x7 == 0x2'
This is equivalent to the C expression:
(sk_buff->_nfct & NFCT_INFOMASK) == IP_CT_NEW
Masks can be specified in any base (hexadecimal, binary, or decimal) and must be positive. While masks up to u64::MAX are allowed, ensuring consistency is your responsibility.

String Comparisons

String values must be enclosed in quotes and only support equality operators:
# Match specific interface name
$ retis collect -m 'sk_buff.dev.name == "eth0"'

# Exclude loopback interface
$ retis collect -m 'sk_buff.dev.name != "lo"'

Type Casting

Follow pointers embedded in members with different defined types:
# Cast and follow pointer to nf_conn structure
$ retis collect -m 'sk_buff._nfct:~0x7:nf_conn.mark'
This accesses the mark field of the nf_conn structure pointed to by _nfct after masking.

Signed vs. Unsigned

Retis supports both signed and unsigned comparisons:
  • Unsigned numbers can use any base (decimal, hex, binary)
  • Signed numbers (negative values) can only use base 10
  • Negative numbers are only allowed against signed struct members
  • Bitfields are supported and treated as regular numbers

Combining Filter Types

You can use packet and metadata filtering together. Both filters must match for an event to be generated:
$ retis collect -f 'tcp port 443' -m 'sk_buff.dev.name == "eth0"'
This command reports only TCP port 443 traffic on the eth0 interface.

Advanced Examples

# Match NEW connections with specific mark
$ retis collect -m 'sk_buff._nfct:0x7 == 0x2 && sk_buff.mark == 0x100'

Next Steps

Tracking

Learn how filtered packets are tracked across the stack

Collectors

Understand what data collectors can provide for filtering

Build docs developers (and LLMs) love