Skip to main content
Retis provides Python bindings that enable writing custom post-processing scripts beyond the basic commands. These bindings work in two ways: through a built-in Python interpreter or as an external Python library.

Requirements

Python >= 3.7 is required. On older distributions where this version is not available, build Retis without Python support:
CARGO_CMD_OPTS="--no-default-features" make

Core Python Classes

The Python bindings provide the following classes for inspecting Retis events:
Python representation of a Retis event with helpers to access sections and their data.
# Access event sections
print(event.sections())
# ['ct', 'skb-tracking', 'common', 'skb', 'kernel']

# Access specific section data
if event.kernel:
    print(event.kernel.symbol)

# Convert to dictionary
event_dict = event.to_dict()
Python representation of a series of sorted events from retis sort -o. Implements the iterator protocol.
for series in reader.series():
    print(f"Series has {len(series)} events")
    for event in series:
        # Process each event
        pass
Reads a file created by retis collect and iterates over events.
events = reader.events()
for event in events:
    if event.packet:
        print(event.packet.to_scapy().summary())
Reads a file created by retis sort and iterates over event series.
from retis import SeriesReader

reader = SeriesReader("sorted_events.json")
for series in reader:
    print(f"Processing series with {len(series)} events")
Automatically determines if a file is sorted or unsorted and creates appropriate reader instances.
# Check if file is sorted
if reader.sorted():
    series = reader.series()
else:
    events = reader.events()

Built-in Python Interpreter

The built-in interpreter executes Python scripts with automatic access to event data through a global reader variable of type EventFile.

Basic Script Execution

# myscript.py
for event in reader.events():
    if event.kernel:
        print(f"event from {event.kernel.symbol}")
retis python myscript.py

Script Storage Locations

Store scripts in these directories to execute by name:
  • $HOME/.config/retis/python
  • /usr/share/retis/python
ls $HOME/.config/retis/python
# foo.py
retis python foo

Interactive Shell

Launch an interactive Python shell with automatic event file access:
retis collect [...]
retis python
Python 3.12.6 (main, Sep  9 2024, 00:00:00) [GCC 14.2.1 20240801 (Red Hat 14.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> reader
<builtins.EventFile object at 0x7f1943606030>
>>> reader.sorted()
False
>>> events = reader.events()
>>> print(f"Got {sum(1 for _ in events)} events")
Got 783 events

Working with Sorted Files

retis sort -o sorted.data
retis python -i sorted.data
>>> reader.sorted()
True
>>> series = reader.series()
>>> print(f"Got {sum(1 for _ in series)} series")
Got 149 series

Command Line Arguments

Scripts have full access to sys.argv and can use standard modules like argparse:
# argv.py
print(sys.argv)
retis python argv.py
# ['argv.py']
retis python argv.py foo
# ['argv.py', 'foo']
retis python -- argv.py -x --foo
# ['argv.py', '-x', '--foo']

External Python Library

For sophisticated programs requiring control over the Python environment, use the retis package on PyPI.

Installation

pip install retis

Example: Series Statistics

from retis import SeriesReader
import statistics

reader = SeriesReader("sorted_events.json")
events_per_series = [len(s) for s in reader]

print(f"Number of series: {len(events_per_series)}")
print(f"Average events per series: {statistics.mean(events_per_series)}")

Parsing Packet Data

Packet data is stored raw in Retis events. Use a third-party library like Scapy to parse individual packet fields.

Using Scapy Helper

Retis provides a to_scapy() helper to convert raw packets to Scapy Ether objects:
retis python
>>> events = reader.events()
>>> e = next(events)  # Skip startup event
>>> e = next(events)
>>> p = e.packet.to_scapy()
>>> print(p.summary())
Ether / IP / TCP 1.1.1.1:https > 10.0.0.42:12345 A
>>> if IP in p:
...     print(f"src: {p[IP].src}")
src: 1.1.1.1
>>> if TCP in p:
...     print(p[TCP].options[2])
('Timestamp', (3570509991, 2706919))

Importing Scapy

# Scapy is automatically imported
p = event.packet.to_scapy()

Raw Packet Access

Access raw packet data as bytes for custom parsing:
# As bytes string
event.packet.data

# As bytes object
bytes(event.packet.data)

# Manual Scapy conversion
from scapy.layers.l2 import Ether
Ether(bytes(event.packet.data))

Available Helpers

Event Sections

# List all sections in an event
print(event.sections())
# ['ct', 'skb-tracking', 'common', 'skb', 'kernel']

# Convert to dictionary
event_dict = event.to_dict()
print(event_dict.keys())
# dict_keys(['skb-tracking', 'kernel', 'common', 'ct', 'skb'])
print(event_dict['common'].keys())
# dict_keys(['timestamp'])

Event Display

# Print full event
print(event)
# 8974965787422 (5) [ping] 100854 [tp] net:net_dev_start_xmit #829a5a5cb1effff8be0ca834000 (skb ffff8be0ca56ae00)
#   if 3 (eth0)
#   xx:xx:xx:xx:xx:xx > xx:xx:xx:xx:xx:xx ethertype IPv4 (0x0800) 10.0.42.5 > 1.1.1.1 ...

# Print specific sections
print(event.common)
# 8974965787422 (5) [ping] 100854

print(event.ct)
# ct_state NEW status 0x188 icmp orig [10.0.42.5 > 1.1.1.1 type 8 code 0 id 1] ...

# Get raw representation
print(repr(event.common))
# {'task': {'comm': 'irq/184-iwlwifi', 'tgid': 1632, 'pid': 1632}, 'smp_id': 7, 'timestamp': 6876861762597}

SKB Tracking

# Get tracking ID
hex(event.skb_tracking.tracking_id())
# '0x829a5a5cb1effff8be0ca834000'

# Match on tracking ID (including clones)
e0.skb_tracking.match(e1.skb_tracking)
# True

# Match on tracking ID + skb address (exact match)
e0.skb_tracking.strict_match(e1.skb_tracking)
# False

See Also

Build docs developers (and LLMs) love