courses

Network Flow Analysis: NetFlow, Zeek, and Argus

Metadata at scale: trading payload for retention

Full packet capture stores every byte on the wire and is indispensable for deep forensic analysis, but it does not scale to 10 Gbps links or months of data. Flow-based analysis trades packet-level detail for long-term visibility: instead of storing raw bytes, you store metadata about each conversation — who talked to whom, for how long, how many bytes, and which protocol.

This page covers three complementary approaches to flow-based network analysis:

For PCAP manipulation tools and an introduction to running Zeek and Argus against existing captures, see Pcap Analysis and Manipulation Tools.

NetFlow and IPFIX

Protocol overview

NetFlow was introduced by Cisco in the early 1990s to give routers the ability to report summary statistics about traffic traversing them. A flow is defined by a 5-tuple (source IP, destination IP, source port, destination port, protocol) plus ingress interface. When a flow ends (TCP FIN/RST, or an idle/active timeout), the router exports a flow record to a collector.

Version Notes
v1 Original; fixed 12-field record; no longer used
v5 Most widely supported; fixed 24-byte record per flow; IPv4 only
v9 Template-based; extensible; supports IPv6, MPLS, VLANs
IPFIX (RFC 7011) IETF standard derived from v9; adds variable-length fields, enterprise extensions

IPFIX is the current standard. Cisco hardware still labels it “NetFlow v9” or “Flexible NetFlow”, but modern collectors and exporters all speak IPFIX. Use IPFIX/v9 for new deployments.

What a flow record contains

A basic v9/IPFIX flow record includes:

Field Description
flowStartMilliseconds First packet timestamp
flowEndMilliseconds Last packet timestamp
sourceIPv4Address Source IP
destinationIPv4Address Destination IP
sourceTransportPort Source port
destinationTransportPort Destination port
protocolIdentifier IP protocol number (6=TCP, 17=UDP, 1=ICMP)
tcpControlBits Bitwise OR of all TCP flags seen in the flow
packetDeltaCount Packets in this flow
octetDeltaCount Bytes in this flow
ingressInterface SNMP interface index

Flow records intentionally omit payload — they are metadata only. This limits their utility for DPI but makes them suitable for long-term retention (a 1 Gbps link generates roughly 10–50 MB of flow data per hour versus 450 GB of full packets).

Generating NetFlow from Linux

Routers and switches with NetFlow/IPFIX support export flow records natively. On a Linux host or VM, use one of these software exporters:

softflowd

softflowd reads packets from a live interface or PCAP file and exports flow records to a collector.

sudo apt install softflowd

# Export NetFlow v9 to a local collector on UDP 2055
sudo softflowd -i eth0 -n 127.0.0.1:2055 -v 9

# Export from a PCAP file
sudo softflowd -r capture.pcap -n 127.0.0.1:2055 -v 9 -D

# Key flags:
# -i <iface>     capture interface
# -r <file>      read from PCAP instead of live capture
# -n <host:port> collector address
# -v <version>   NetFlow version: 1, 5, or 9
# -D             don't daemonise (useful for testing)
# -T <timeout>   active flow timeout in seconds (default 60)

nfpcapd (part of nfdump)

nfpcapd converts live traffic or a PCAP directly to nfdump binary files — no separate collector needed. It is the simplest path if you only want nfdump analysis:

sudo apt install nfdump

# Capture live traffic and write nfdump files, rotating every 5 minutes
sudo nfpcapd -i eth0 -l /var/lib/nfdump/eth0 -T +all -S 5

# Convert an existing PCAP to nfdump format
nfpcapd -r capture.pcap -l /tmp/nfout/

# Key flags:
# -i <iface>   live capture interface
# -r <file>    read from PCAP
# -l <dir>     output directory for nfdump files
# -T +all      include all available flow fields (recommended)
# -S <mins>    rotate output file every N minutes

Collecting NetFlow with nfcapd

When routers or softflowd are exporting flow records across the network, use nfcapd as the collector daemon:

# Listen on UDP 2055 and rotate files every 5 minutes
sudo nfcapd -w -D -l /var/lib/nfdump/router1 -p 2055 -S 5

# Key flags:
# -w           write to subdirectory tree: YYYY/MM/DD/nfcapd.YYYYMMDDhhmm
# -D           daemonise
# -l <dir>     base output directory
# -p <port>    UDP port to listen on (default 9995)
# -b <addr>    bind to this IP (useful on multi-homed collectors)
# -S <mins>    file rotation interval

# Receive from multiple exporters on different ports
sudo nfcapd -l /var/lib/nfdump/router1 -p 2055 &
sudo nfcapd -l /var/lib/nfdump/router2 -p 2056 &

Output directory structure after rotation:

/var/lib/nfdump/router1/
├── 2026/
│   └── 05/
│       └── 12/
│           ├── nfcapd.202605121400
│           ├── nfcapd.202605121405
│           └── nfcapd.202605121410

Analyzing flows with nfdump

nfdump reads the binary files written by nfcapd or nfpcapd and supports ad-hoc queries with a tcpdump-like filter syntax plus aggregation and statistics.

# Print all flows in a file
nfdump -r /var/lib/nfdump/router1/2026/05/12/nfcapd.202605121400

# Print all flows for a time range (reads multiple files)
nfdump -R /var/lib/nfdump/router1/2026/05/12 \
  -t '2026-05-12 14:00:00-2026-05-12 15:00:00'

# Apply a filter (syntax similar to tcpdump BPF)
nfdump -r nfcapd.202605121400 'src ip 192.168.1.42'
nfdump -r nfcapd.202605121400 'dst port 443'
nfdump -r nfcapd.202605121400 'proto tcp and src net 10.0.0.0/8'
nfdump -r nfcapd.202605121400 'flags S and not flags AFRPU'  # SYN-only (scan detection)

# Show only flows with > 1 MB transferred
nfdump -r nfcapd.202605121400 'bytes > 1000000'

Output formatting:

# Default output: one line per flow (ts, duration, proto, src->dst, flags, bytes, pps)
nfdump -r nfcapd.202605121400

# Custom output fields
nfdump -r nfcapd.202605121400 -o 'fmt:%ts %sa:%sp -> %da:%dp %pkt %byt %flg'
# %ts=start %sa=src addr %sp=src port %da=dst addr %dp=dst port
# %pkt=packets %byt=bytes %flg=TCP flags

# CSV output for scripting
nfdump -r nfcapd.202605121400 -o csv 'dst port 53'

Aggregation and top-N statistics:

# Top 10 talkers by bytes (sum flows per source IP)
nfdump -r nfcapd.202605121400 -s srcip/bytes -n 10

# Top 10 destination ports by flow count
nfdump -r nfcapd.202605121400 -s dstport/flows -n 10

# Top 10 source/destination pairs by packets
nfdump -r nfcapd.202605121400 -s srcip,dstip/pkts -n 10

# Aggregate all flows for the same 5-tuple (collapse retransmits)
nfdump -r nfcapd.202605121400 -A srcip,dstip,srcport,dstport,proto

# Count unique destination IPs per source (high count = scanner)
nfdump -r nfcapd.202605121400 -s srcip/dstip -n 20 -o 'fmt:%sa %td'

Security analysis patterns:

# Find port scanners: sources hitting many destination ports
nfdump -r nfcapd.202605121400 -s srcip/dstport -n 10 \
  'flags S and not flags AFRPU'

# Large outbound transfers (possible exfiltration)
nfdump -r nfcapd.202605121400 \
  'src net 10.0.0.0/8 and dst net not 10.0.0.0/8 and bytes > 10000000' \
  -s srcip,dstip/bytes -n 10

# All DNS traffic to non-corporate resolvers (possible DNS tunneling)
nfdump -r nfcapd.202605121400 \
  'dst port 53 and dst ip not 10.0.0.0/8' \
  -s dstip/flows -n 20

# Long-duration flows (beaconing, tunnels, C2 keepalive)
nfdump -r nfcapd.202605121400 -o 'fmt:%ts %td %sa -> %da:%dp %byt' \
  'duration > 3600'

# Flows with only SYN set and no data (SYN scan or incomplete handshake)
nfdump -r nfcapd.202605121400 'flags S and not flags AF and bytes < 100'

Zeek

Zeek (formerly Bro) is a network analysis framework that runs protocol analyzers against live traffic or PCAP files and produces structured, tab-separated log files for every protocol transaction it observes. Unlike NetFlow, Zeek logs application-layer details — HTTP method and response code, TLS certificate subject, DNS query and answer, SMTP envelope — not just 5-tuples.

The current LTS release is Zeek 7.0.x (LTS); Zeek 8.x is the current feature release.

Installation

# Debian/Ubuntu — add OISF repository
echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/ /' \
  | sudo tee /etc/apt/sources.list.d/security:zeek.list
curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_22.04/Release.key \
  | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security_zeek.gpg > /dev/null
sudo apt update
sudo apt install zeek

# FreeBSD
pkg install zeek

# Verify
zeek --version
# Zeek version 7.0.8

The package installs Zeek under /opt/zeek/ by default. Add /opt/zeek/bin to your PATH:

echo 'export PATH=/opt/zeek/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

Running Zeek

Against a PCAP:

# Produces log files in the current directory
zeek -r capture.pcap

# With a specific policy script
zeek -r capture.pcap /opt/zeek/share/zeek/policy/frameworks/files/extract-all-files.zeek

# Suppress informational output
zeek -r capture.pcap -C 2>/dev/null
# -C disables checksum validation (necessary for many VM captures)

Live capture on an interface:

sudo zeek -i eth0

# Run as a service via ZeekControl (manages multiple workers)
sudo zeekctl deploy    # deploy configuration and start
sudo zeekctl status
sudo zeekctl stop

ZeekControl configuration lives in /opt/zeek/etc/node.cfg:

# /opt/zeek/etc/node.cfg — single-node deployment
[manager]
type=manager
host=localhost

[proxy-1]
type=proxy
host=localhost

[worker-1]
type=worker
host=localhost
interface=eth0

Log files

Zeek produces one log file per protocol. Logs rotate hourly and are compressed by default under /opt/zeek/logs/. The most important logs for security analysis:

Log Contents
conn.log One record per completed TCP/UDP/ICMP flow: endpoints, duration, bytes, conn_state
http.log HTTP requests: method, URI, user agent, response code, response body MIME type
dns.log DNS queries and answers: queried name, type, TTL, resolved addresses
ssl.log TLS sessions: SNI, certificate subject/issuer, JA3/JA3S, cipher suite
files.log Files observed in HTTP/SMTP/FTP: MIME type, MD5/SHA1 hash, extraction path
x509.log X.509 certificate details from TLS handshakes
smtp.log Email transactions: envelope from/to, subject, MIME type, attachment filenames
ssh.log SSH sessions: client/server version, auth result, HASSH fingerprint
notice.log Alerts raised by Zeek policy scripts
weird.log Protocol anomalies Zeek observed (malformed headers, unexpected state, etc.)
capture_loss.log Estimated packet loss percentage (nonzero = unreliable analysis)

Log format: tab-separated values with a #fields header line and a #types line. The uid field is present in every log and links related records from different log files (an HTTP request in http.log and its TLS session in ssl.log share the same uid).

conn.log fields

ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service
duration orig_bytes resp_bytes conn_state local_orig local_resp
missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes

conn_state encodes the TCP handshake outcome:

Value Meaning
S0 SYN sent, no response — port scan, dropped packet
S1 Established, not closed (truncated capture)
SF Normal SYN-SYNACK-data-FIN close
REJ SYN sent, RST received — port closed
RSTO Established, originator sent RST
RSTR Established, responder sent RST
OTH Mid-stream traffic with no setup/teardown

history is a sequence of letters encoding events: S=SYN, h=SYNACK, D=data, F=FIN, R=RST (lowercase = responder’s event).

Querying logs with zeek-cut

zeek-cut extracts columns from Zeek TSV logs by field name:

# Most-talked-to external hosts, by connection count
zeek-cut id.resp_h id.resp_p < conn.log \
  | sort | uniq -c | sort -rn | head 20

# Long-duration connections (possible C2 beaconing)
zeek-cut ts id.orig_h id.resp_h id.resp_p duration < conn.log \
  | awk '$5 > 3600' \
  | sort -k5 -rn

# Total bytes transferred per destination IP
zeek-cut id.resp_h orig_bytes resp_bytes < conn.log \
  | awk '{b[$1]+=$2+$3} END {for(h in b) print b[h], h}' \
  | sort -rn | head 10

# All User-Agent strings observed
zeek-cut user_agent < http.log | sort | uniq -c | sort -rn

# HTTP requests with non-2xx responses to internal hosts
zeek-cut ts id.orig_h id.resp_h method uri status_code < http.log \
  | awk '$6 >= 400'

# DNS queries for non-standard TLDs
zeek-cut query answers < dns.log \
  | awk '!/\.(com|net|org|io|gov|edu)$/'

# All unique TLS SNI values
zeek-cut server_name < ssl.log | sort -u

# Connections in S0 state (incomplete — scanner activity)
zeek-cut id.orig_h id.resp_h id.resp_p conn_state < conn.log \
  | awk '$4 == "S0"' | sort | uniq -c | sort -rn | head 20

# Join conn.log and http.log on uid to see bytes per HTTP host
join -1 2 -2 1 \
  <(zeek-cut uid orig_bytes resp_bytes < conn.log | sort -k1,1) \
  <(zeek-cut uid host method uri < http.log | sort -k1,1) \
  | awk '{print $4, $2+$3}' | sort | uniq | sort -k2 -rn | head 10

Zeek Package Manager (zkg)

zkg installs third-party Zeek scripts and plugins:

# Refresh the package list
zkg refresh

# Search for a package
zkg search ja3

# Install a package
zkg install zeek/salesforce/ja3

# List installed packages
zkg list installed

# Update all packages
zkg upgrade

Useful community packages:

Package Purpose
zeek/salesforce/ja3 JA3/JA3S TLS fingerprinting
zeek/corelight/zeek-community-id Community ID flow hash (correlates with Suricata/Elastic)
zeek/corelight/bro-xor-exe Detect XOR-encoded executables in HTTP responses
zeek/mitre-attack/bzar Detect ATT&CK techniques in SMB/DCERPC traffic

Zeek scripting

Zeek’s built-in scripting language allows you to define event handlers that fire when Zeek processes specific protocol events, write new log streams, raise notices, and call external programs. Scripts live in /opt/zeek/share/zeek/site/ and are loaded via /opt/zeek/share/zeek/site/local.zeek.

Language basics

# Types: bool, int, count, double, string, time, interval, addr, subnet, port
# Containers: table, set, vector, record

# Variable declaration (global)
global dns_query_counts: table[addr] of count &default=0;

# Module declaration
module MyDetection;

# Define a new log stream
export {
    redef enum Log::ID += { LOG };

    type Info: record {
        ts:      time   &log;
        src:     addr   &log;
        reason:  string &log;
    };
}

event zeek_init() &priority=5 {
    Log::create_stream(MyDetection::LOG,
        [$columns=Info, $path="my_detection"]);
}

Common event handlers

# Fires for every completed connection
event connection_state_remove(c: connection) {
    # c$id.orig_h, c$id.resp_h, c$id.resp_p
    # c$conn$duration, c$conn$orig_bytes, c$conn$resp_bytes
}

# Fires for every HTTP request
event http_request(c: connection, method: string, original_URI: string,
                   unescaped_URI: string, version: string) {
    # c$id.orig_h = client, c$id.resp_h = server
}

# Fires for every HTTP reply
event http_reply(c: connection, version: string, code: count, reason: string) { }

# Fires for every DNS query
event dns_request(c: connection, msg: dns_msg, query: string,
                  qtype: count, qclass: count) { }

# Fires when a TLS handshake completes
event ssl_established(c: connection) {
    local ssl = c$ssl;
    # ssl$server_name, ssl$subject, ssl$issuer, ssl$ja3, ssl$ja3s
}

# Fires when Zeek extracts a file from a connection
event file_new(f: fa_file) { }

Example script: detect HTTP beaconing by frequency

# /opt/zeek/share/zeek/site/beacon-detect.zeek
# Raise a notice when a source IP makes more than 20 identical HTTP requests
# to the same host within a 5-minute window.

module BeaconDetect;

export {
    redef enum Notice::Type += { Beacon };
}

# Track (src_ip, dst_host) -> count within a window
global request_counts: table[addr, string] of count &default=0 &create_expire=5min;

event http_request(c: connection, method: string, original_URI: string,
                   unescaped_URI: string, version: string) {
    local src  = c$id$orig_h;
    local host = c$http$host;

    ++request_counts[src, host];

    if ( request_counts[src, host] == 20 ) {
        NOTICE([$note=BeaconDetect::Beacon,
                $conn=c,
                $msg=fmt("%s may be beaconing to %s (%d requests in 5 min)",
                         src, host, request_counts[src, host]),
                $identifier=fmt("%s-%s", src, host)]);
    }
}

Load it:

echo '@load site/beacon-detect' >> /opt/zeek/share/zeek/site/local.zeek
sudo zeekctl deploy

Example script: log all executables downloaded over HTTP

# /opt/zeek/share/zeek/site/log-exe-downloads.zeek
# Add a notice to files.log when an executable is transferred over HTTP.

event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) {
    if ( f?$mime_type && f$mime_type == "application/x-dosexec" ) {
        NOTICE([$note=Notice::Tally,
                $conn=c,
                $msg=fmt("PE executable download from %s", c$id$resp_h),
                $identifier=cat(c$id$resp_h)]);
    }
}

Argus

Argus (Audit Record Generation and Utilization System) is a high-performance flow recorder that captures bidirectional network sessions and stores them in a compact binary format. Where NetFlow is a push protocol (routers export to a collector), Argus runs on a sensor host and captures directly from an interface or PCAP.

Argus captures significantly more state than NetFlow: it records the full TCP state machine, tracks retransmissions, round-trip time, jitter, and loss on a per-flow basis. The argus-clients package provides a rich set of tools for reading, filtering, aggregating, and distributing Argus data.

Installation

sudo apt install argus-server argus-client

# Verify
argus -h 2>&1 | head -3
ra -h 2>&1 | head -3

Live capture with argus

# Capture from interface, write to file
sudo argus -i eth0 -w /var/log/argus/flows.argus

# Capture and write rotating files (new file every hour)
sudo argus -i eth0 -w /var/log/argus/argus.%Y%m%d%H%M.argus

# Capture from PCAP
argus -r capture.pcap -w flows.argus

# Capture with Argus serving as a real-time stream on TCP 561
sudo argus -i eth0 -P 561 -d
# -d daemonise; -P <port> listen for ra clients on this port

# Key flags:
# -i <iface>   capture interface
# -r <file>    read from PCAP
# -w <file>    write flow data; use %Y%m%d%H%M for strftime rotation
# -P <port>    serve real-time stream to ra clients
# -d           daemonise
# -T <secs>    idle flow timeout (default 60s)
# -t <secs>    active flow timeout (default 600s)

ra — read and filter flows

ra is the primary client for reading Argus data. It prints flow records and supports a BPF-like filter language.

# Print all flows (default one-line format)
ra -r flows.argus

# Print specific fields
ra -r flows.argus -s stime dur proto src sport dst dport bytes pkts

# Apply a filter
ra -r flows.argus -s stime dur src dst proto dport bytes \
  - tcp and dst port 443

# Connect to a live argus stream
ra -S 192.168.1.100:561

# Filter: all TCP flows over 1 MB
ra -r flows.argus - 'tcp and bytes > 1000000'

# Filter: connections from a specific host
ra -r flows.argus - 'src host 10.0.0.42'

# Filter: connections to a destination port range
ra -r flows.argus - 'dst portrange 8000-8080'

# Custom output separator (for scripting)
ra -r flows.argus -c ',' -s stime dur proto src dst dport bytes pkts

Available display fields (-s argument):

Field Description
stime Flow start timestamp
ltime Flow last-seen timestamp
dur Duration
proto Protocol (tcp, udp, icmp…)
src Source IP
sport Source port
dst Destination IP
dport Destination port
bytes Total bytes (both directions)
sbytes Source-to-destination bytes
dbytes Destination-to-source bytes
pkts Total packets
spkts Src-to-dst packets
dpkts Dst-to-src packets
state TCP state (CON, REQ, RST, FIN, TIM…)
flgs TCP flags
sttl Source-to-destination TTL
dttl Destination-to-source TTL
swin TCP window size (src)
dwin TCP window size (dst)
srtt Smoothed round-trip time
jitter Jitter (RTT variance)
loss Estimated packet loss

racluster — aggregate flows

racluster aggregates flow records based on a configurable key, collapsing many individual records into summaries.

# Aggregate by source IP (one record per unique source)
racluster -r flows.argus -m saddr

# Aggregate by source/destination pair
racluster -r flows.argus -m saddr daddr

# Aggregate by source IP and destination port (service usage per host)
racluster -r flows.argus -m saddr dport -s saddr dport bytes pkts

# Aggregate by destination port across all flows (service totals)
racluster -r flows.argus -m dport -s dport bytes pkts flows \
  | sort -t' ' -k3 -rn | head 20

# Top talkers by bytes (aggregate all flows per src/dst pair, sort)
racluster -r flows.argus -m saddr daddr -s saddr daddr bytes \
  -c ',' | sort -t',' -k3 -rn | head 10

# Security: hosts that sent SYN to many unique destinations (scanners)
racluster -r flows.argus -m saddr daddr \
  - 'tcp and flgs S' | racount -s saddr | sort -k2 -rn | head 10

racount — count records and bytes

# Summary statistics for the entire file
racount -r flows.argus

# Count flows per protocol
racount -r flows.argus -s proto

# Count flows per source IP
racount -r flows.argus -m saddr -s saddr

rasort — sort flows

# Sort by duration, descending
rasort -r flows.argus -s dur -M rsort

# Sort by bytes
rasort -r flows.argus -s bytes -M rsort | ra -s stime src dst dport bytes

# Sort and display top 10 by bytes
rasort -r flows.argus -s bytes -M rsortn,10

rabins — time-bin analysis

rabins divides the flow stream into fixed time windows and produces one summary record per bin. This is the right tool for detecting beaconing (regular intervals) or plotting traffic volume over time.

# 5-minute bins, count flows and bytes per bin
rabins -r flows.argus -M time 5m -s stime bytes pkts

# 1-minute bins for a specific src/dst pair
rabins -r flows.argus -M time 1m -s stime dur bytes \
  - 'src host 10.0.0.42 and dst host 8.8.8.8'

# Detect beaconing: source IPs that appear in every 5-min bin for an hour
rabins -r flows.argus -M time 5m -s stime saddr \
  | sort | uniq -c | sort -rn

rasplit — partition flow data

rasplit divides a flow file into multiple files based on count, time, or flow event — useful for archiving and parallel processing.

# Split into 1-hour files
rasplit -r flows.argus -M time 1h -w /archive/argus.%Y%m%d%H.argus

# Split into files of at most 100,000 records
rasplit -r flows.argus -M count 100000 -w /archive/split.%05d.argus

# Split into one file per source IP (useful for per-host analysis)
rasplit -r flows.argus -M saddr -w /archive/%saddr.argus

radium — distribute and relay

radium is a flow distribution daemon. It reads from one or more Argus sources (files, live streams, or a combination) and re-serves the merged stream to multiple downstream clients. Use it to fan out a single capture point to several analysts or analytics pipelines without duplicating the capture process.

# Relay a live argus stream to multiple clients
sudo radium -S 192.168.1.10:561 -P 562 -d
# -S reads from argus daemon on .10:561
# -P serves merged stream to clients on local port 562

# Merge two files and serve the combined stream
radium -r morning.argus -r afternoon.argus -P 563

# Apply a filter before distribution (reduce bandwidth to clients)
radium -S 192.168.1.10:561 -P 562 - 'not icmp'

Security analysis with Argus

Detect port scanning (S0-equivalent: SYN sent, no SYN-ACK):

# Flows where TCP flags is exactly 'S' (SYN only, no handshake)
racluster -r flows.argus -m saddr daddr dport \
  - 'tcp and flgs S and not flgs SA' \
  | racount -s saddr | sort -k2 -rn | head 10

Identify long-lived connections (C2 keepalive, tunnels):

ra -r flows.argus -s stime dur src dst dport bytes \
  - 'dur > 3600' | sort -k2 -rn

Find data exfiltration candidates (large outbound transfers):

ra -r flows.argus -s stime src dst dport sbytes \
  - 'sbytes > 10000000 and not dst net 10.0.0.0/8' \
  | sort -k5 -rn | head 10

Round-trip time anomalies (possible geographic routing change or MITM):

ra -r flows.argus -s stime src dst dport srtt \
  - 'tcp and srtt > 500'   # RTT > 500ms in milliseconds

Comparing the Three Approaches

Capability NetFlow/IPFIX Zeek Argus
Application-layer content No Yes No
TCP state machine detail Partial (flags only) Yes (conn_state, history) Yes (full state, RTT, loss)
File extraction No Yes No
Scripting / detection No Yes (Zeek scripts) No
Protocol logs (HTTP, DNS…) No Yes No
Long-term flow retention Excellent (compact) Moderate (TSV, compressible) Excellent (binary)
Router/switch native export Yes No No
Real-time alerting Via tools (nfsen) Yes (notices) Via radium pipelines
IPv6 support IPFIX: full; v5: no Yes Yes

When to use each:

Key takeaways

References


Related course pages: Pcap analysis and manipulation tools · Analyzing traffic with Wireshark · tshark on the command line · Capturing network traffic

🛠️ Maintenance note: Zeek versions move yearly — the page cites 7.0.x LTS / 8.x feature; confirm the current LTS and feature releases (and the openSUSE repo URL / Ubuntu codename in the install block) each term. nfdump flag syntax and Argus client options also shift between releases, and the directory-date examples (2026/05/12) are illustrative.