Rockfish Suricata Transport Performance Plugin

Per-flow TCP and UDP performance metrics, emitted as tcp_perf and udp_perf events through Suricata’s normal eve-log pipeline. Whatever filetype: your eve-log uses (regular file, unix_dgram, unix_stream, syslog, redis) — these events go there too, mixed in with your existing flow / dns / tls events.

TCP signals UDP signals
Three-way handshake RTT Request/response RTT (paired)
Retransmits per direction Inter-arrival time (avg, stddev)
Out-of-order packets Per-direction byte/packet counts
Zero-window events  
Window stats (min/avg/max)  
RST / FIN counts  
Termination reason (fin / rst / timeout)  

Build

SURICATA_SRC=/path/to/configured/suricata-src make
# or, if libsuricata-config is on PATH:
make

The result is rockfish-transport-perf.so.

Install

Copy the .so into Suricata’s plugin directory and reference it from suricata.yaml. Then enable tcp_perf and udp_perf under your existing eve-log.types: list — that’s it.

plugins:
  - /usr/lib/suricata/plugins/rockfish-transport-perf.so

outputs:
  - eve-log:
      enabled: yes
      filetype: unix_stream     # or whatever you already use
      filename: /var/run/rockfish/rockfish.sock
      types:
        - alert
        - flow
        - dns
        - tls:
            extended: yes
        # ── Transport perf events ────────────────────────────────
        - tcp_perf
        - udp_perf

# Optional plugin tuning (all keys optional, defaults shown).
rockfish-transport-perf:
  enabled: yes
  tcp: yes
  udp: yes
  sample-rate: 1                     # 1-in-N flow sampling
  max-flows: 100000
  flow-idle-timeout: 60
  udp-rtt-pairing-window-ms: 2000
  emit:
    handshake-rtt: yes
    retransmits:   yes
    zero-window:   yes
    window-stats:  yes
    udp-rtt:       yes
    udp-jitter:    yes

No second socket. No second file. No output-file knob. The plugin hands its events to Suricata’s eve writer and Suricata routes them.

Output format

Sample TCP record (lives in the same eve stream as flow/dns/tls):

{
  "timestamp": "2026-04-28T17:32:11.018452Z",
  "flow_id": 17628341205823,
  "event_type": "tcp_perf",
  "src_ip": "10.1.2.45", "src_port": 49215,
  "dest_ip": "10.1.2.10", "dest_port": 443,
  "proto": "TCP",
  "tcp_perf": {
    "start_us": 1748365931018452,
    "end_us":   1748365941312009,
    "duration_us": 10293557,
    "handshake_rtt_us": 1842,
    "pkts_toserver": 84,  "pkts_toclient": 71,
    "bytes_toserver": 7224, "bytes_toclient": 11502,
    "retransmits_toserver": 1,
    "avg_window_toclient": 64240,
    "min_window_toclient": 60128,
    "max_window_toclient": 65535,
    "fin_count": 2,
    "close_reason": "fin"
  }
}

Sample UDP record:

{
  "timestamp": "2026-04-28T17:32:11.118452Z",
  "flow_id": 17628341219991,
  "event_type": "udp_perf",
  "src_ip": "10.1.2.45", "src_port": 53412,
  "dest_ip": "10.1.2.1", "dest_port": 53,
  "proto": "UDP",
  "udp_perf": {
    "start_us": 1748365931118452,
    "end_us":   1748365931145812,
    "duration_us": 27360,
    "pkts_toserver": 1, "pkts_toclient": 1,
    "bytes_toserver": 64, "bytes_toclient": 188,
    "rtt_count": 1,
    "rtt_min_us": 27188, "rtt_max_us": 27188,
    "rtt_avg_us": 27188.0
  }
}

How rockfish-perf consumes this

rockfish-perf reads the Suricata eve socket and now sees tcp_perf / udp_perf mixed in with flow / dns. The fields surface in the per-asset feature vector as:

Feature Source
tcp_handshake_rtt_ms_avg tcp_perf.handshake_rtt_us
tcp_retransmit_ratio retransmits / packets
tcp_zero_window_ratio zero_window / packets
tcp_out_of_order_ratio out_of_order / packets
udp_rtt_avg_ms udp_perf.rtt_avg_us
udp_jitter_avg_ms mean of per-direction iat_stddev_us

These dimensions are added to the HBOS drift baseline automatically.