Skip to content

Repository files navigation

sloth

CI Code Review Reviewed by GPT-5.2 Codex

2122 test assertions passing Mutation testing: 51.0% of considered mutants killed across 18 files make mutate harness available in-tree

sloth

A terminal-based passive network monitor for Linux, written in C99. Sloth never injects packets, never scans, and never modifies kernel state — it observes what your host already sees and turns it into dozens of live views, a suite of passive alert rules, an embedded WiFi-SIGINT toolkit (PNL aggregation, RSN/cipher/MFP inventory, EAPOL/PMKID capture, hidden-SSID reveal, seqnum-based MAC-randomisation deanonymisation), and an optional JSONL forensic log.

📖 Per-view deep dives live under docs/views/ — each file explains the protocol, shows a text mockup, and lists what to watch for in normal vs anomalous traffic.

v1.1 — WiFi SIGINT — sloth gained six new wireless capabilities on top of the v1.0 monitor: PNL aggregation per client MAC, RSN / cipher / AKM / MFP inventory from beacons, EAPOL / PMKID / 4-way handshake capture with hashcat-22000 export, hidden-SSID reveal, MAC-randomisation sequence-number deanonymisation, and a dashboard alert-hot IP override that paints any IP appearing in a CRIT alert deep-red across every panel for 1 h. See the v1.1 release notes for the full diff.

[1] Interfaces  [2] Connections  [3] WiFi      [4] Packets   [5] Processes
[6] Stats       [7] Probe        [8] ARP       [9] mDNS      [0] NBNS
[d] DHCP        [s] SSDP         [b] Beacons   [a] Deauth    [h] HTTP
[t] TLS         [u] QUIC         [r] DNS       [p] NTP       [i] ICMP
[v] Alerts      [g] Devices      [o] Dashboard [l] OSI stack [?] Help
[x] Twins       [y] KARMA        [z] RADIUS                  ← evil-twin, PineAP & 802.1X lures
[k] PNL         [e] EAPOL        [j] Seqnum    [w] Assoc     ← WiFi SIGINT
[m] Channel                                                  ← per-channel histogram

What you get

Observation

View What it shows
Interfaces Per-interface RX/TX rates, errors/drops, MTU, link speed; sparkline history
Connections Active TCP/UDP sockets with PID, RTT, retransmits, per-conn bandwidth
WiFi Nearby APs from nl80211 scan: signal, channel, encryption
Packets Live pcap capture with BPF filter, hex detail panel, pcap export
Processes Process tree with fold/unfold
Stats Session totals — bytes, packets, rates per interface since reset
Probe 802.11 probe-request sniffer — unassociated clients and the SSIDs they're looking for
ARP Layer-2 neighbour table with OUI vendor lookup
mDNS Bonjour/Zeroconf service table from passive UDP/5353
NBNS NetBIOS Name Service table from UDP/137
DHCP Live DHCP event log: DISCOVER/REQUEST/ACK
SSDP UPnP device table from UDP/1900 NOTIFY / M-SEARCH
Beacons Passive 802.11 beacon sniffer — APs visible to a monitor-mode iface, with pairwise cipher / AKM / MFP status from the RSN IE, and hidden-SSID reveal from probe-responses
Deauth 802.11 deauth/disassoc frames; flood detection per target MAC
HTTP Plaintext HTTP requests: method, host, path
TLS TLS ClientHello log: SNI host, version, and JA3 fingerprint
QUIC QUIC Initial packets: version + DNS-resolved host
DNS DNS query/response log: qname, qtype, answer, NXDOMAIN
NTP NTP traffic: mode, stratum, reference ID
ICMP ICMPv4 + ICMPv6 with named types (Echo, Unreachable, Neigh Sol, …)

Synthesis

View What it shows
Alerts Rule-derived events: port scans, deauth floods, NXDOMAIN bursts, threat-intel domain hits, threat-intel IP hits, periodic beaconing
Devices One record per MAC, joined from ARP/DHCP/Beacons/Probe/Stations with OUI vendor
Dashboard Composite at-a-glance view: interfaces, conns + top hosts, packets, and seven side-panel categories all tiled to fill the terminal
OSI stack Seven-layer synthesis grid: every count sloth observes mapped onto its OSI layer (L7 application protocols, L6 TLS-version histogram, L5 sessions, L4 transport split, L3 host count, L2 ifaces/APs/STAs, L1 probe iface) — the "where is my traffic happening" cheat sheet

WiFi SIGINT (v1.1)

View What it shows
PNL Per-MAC Preferred Network List — every directed probe-request's source MAC aggregated with the unique set of SSIDs it has probed for. Randomised MACs are flagged so randomised vs burned-in is one glance. A device's PNL fingerprints its owner.
EAPOL Captured EAPOL-Key frames + 4-way handshake state machine. M1 with a PMKID KDE = one-frame offline-crack vector. M1+M2 together = full handshake. --eapol-dir DIR writes captures in hashcat 22000 format.
Seqnum Sequence-number-based MAC-randomisation deanonymisation. Pairs of MACs whose seqnum trails overlap within 64 seqnums / 30 s are the same physical radio across a MAC rotation.
Assoc Client ↔ AP association inventory. Each row is a (BSSID, STA) pair we've observed confirmation for: EAPOL handshake completed, assoc-response status=0, or reassoc-response status=0. Disassoc / deauth removes the entry.

Output

  • sloth -o FILE — append a JSONL line for every DNS/TLS/QUIC/HTTP/NTP/ICMP record and every newly-fired alert. See JSONL schema below.
  • sloth --data-socket [SPEC] — same JSONL records, served over a read-only stream socket (unix:/path or tcp:HOST:PORT) for live consumers. Bare --data-socket with no SPEC defaults to tcp:127.0.0.1:8765 (loopback only). tail -f-style with no filesystem polling. Read-only by design — nothing flows back from the wire (see MISSION.md §4). Multi-client (up to 16). Backpressure is per-client: a slow consumer loses lines, healthy ones keep up. Full schema and consumer notes in docs/wiki/jsonl-schema.md. When the socket is bound to a routable address, sloth advertises it over mDNS (_sloth._tcp) so the sloth-ios client can discover it by name — via an Avahi service file (sloth transmits nothing; avahi-daemon announces). Loopback/UNIX sockets never advertise; disable entirely with --no-discovery. This is the sole opt-out carve-out in MISSION.md §2.
  • sloth --pcap-dir DIR — when a rule fires with a known flow identifier (THREAT_IP, BEACONING, PORT_SCAN, NXDOMAIN_BURST, THREAT_DOMAIN), the matching packets are written to a per-alert pcap file under DIR.
  • sloth --eapol-dir DIR — append each captured PMKID and 4-way handshake to DIR/eapol.22000 in hashcat mixed format. Crack directly with hashcat -m 22000 eapol.22000 wordlist.txt. Sloth also writes a per-handshake DIR/<bssid>_<sta>.pcap (raw 802.11, DLT 105) so each capture can be replayed through aircrack-ng -w wordlist.txt -e <SSID> <file>.pcap or opened in Wireshark.

Headless operation

  • sloth --headless — draw nothing and never touch the terminal: no screen clears, no escape sequences, no raw-mode termios change, no key handling. Capture, alerting and every sink (-o, --data-socket, --db, --report) run exactly as normal. This is the mode for appliance and systemd deployments where the output is a journal, not a screen.

    sloth warns (but still runs) if --headless is given with no sink configured, since that run produces nothing observable.

  • sloth --no-color — keep drawing but emit no colour escape sequences. Also honoured via the NO_COLOR environment variable (no-color.org). Useful over a serial console or when capturing a session to a file.

    Note for existing headless deployments. Before this, running the no-ncurses build with stdin redirected (systemd, < /dev/null) made the poll loop spin: select() reported the EOF stdin readable immediately, so the refresh interval never applied. Measured at ~76 000 redraws in two seconds against an intended ~10, which both burned a core and flooded the journal. tui_poll_key now waits on stdin only when it is a terminal, so this is fixed for --no-color and interactive runs too — not just under --headless.

Persistent state (--db)

  • sloth --db FILE — persist entity state to a SQLite database (off by default). -o and --data-socket are unchanged and remain the wire format; this is the retained artifact.

    A -o run writes ~38 GB/day because snapshot rows re-serialise every poll. The same information as durable state is megabytes, because sloth's cardinality is bounded by construction — every entity table in include/sloth.h is a fixed array, so rows are upserted per entity with first_seen / last_seen rather than appended per tick. That pair is what makes the file a history: "who was here between 2 and 4 AM" becomes a range scan instead of a log grep.

    Read it with the sqlite3 CLI. sloth exposes no query surface — the database is never served over the data socket and gets no RPC (MISSION.md §4). Build without it via make WITH_SQLITE=0; make embedded already excludes it.

    sudo ./sloth --hop --db /var/lib/sloth/sloth.db
    sqlite3 /var/lib/sloth/sloth.db \
      "SELECT mac, ssid FROM pnl_ssids WHERE ssid='CorpWiFi';"

    Repeat surveys: each run records a session, and --report grows a New since last survey section listing APs, devices and probe clients first seen since the previous visit. --site-label names the site. This works because first_seen is preserved across visits, so carried-over entities are excluded rather than re-reported.

    --db-interval-secs N sets the write cadence (default 1).

    Retention is tiered, because not all rows are worth the same on a disk that is filling up. --db-retain-days N (default 30) sets the window for observation rows; entities keep that and alerts plus credential exposures keep 12×. The order reflects what an investigator reaches for months later: what fired outlives who was here, which outlives the individual observations.

    --db-max-mb N (default 512, 0 = unlimited) is a hard ceiling. On breach the oldest observation rows go first — entity, alert and credential rows are never dropped by this guard. A sensor that fills its disk should lose telemetry, not the findings the disk was being kept for. If pruning every eligible row still leaves the file over the ceiling, that is reported once and the file is allowed to exceed it, because the alternative is discarding evidence to satisfy a number.

    Schema-level guardrails from MISSION.md §2 are enforced by the test suite: no password column on credential exposures, no PMKID / nonce / MIC columns anywhere — crackable material stays in the --eapol-dir file the operator explicitly asked for.

Designating your own network

  • sloth --my-ssid SSID / sloth --my-bssid BSSID (both repeatable, max 16 each) — tell sloth which networks are yours. This is a labelling input only: nothing about capture changes and nothing is transmitted, so the passive guarantee in MISSION.md §2 is untouched.

    It answers the question sloth previously could not be asked — is anyone reconnoitring my network? A client whose Preferred Network List names a designated SSID while it is not associated to that network raises MY_NET_RECON. Association is the exoneration, checked by designated BSSID or SSID, so your own users never trip it and you do not have to enumerate every BSSID of a multi-AP deployment.

    Designations also sharpen two existing detectors: deauth and auth floods aimed at a designated BSSID escalate WARN → CRIT, and a designated BSSID is never named the impostor half of an evil-twin pair (which the RSSI heuristic would otherwise often get backwards, since your own AP is usually the closest one).

    sudo ./sloth --hop --my-ssid CorpWiFi --my-bssid aa:bb:cc:dd:ee:ff

    With nothing designated every check is inert and behaviour is unchanged.

Known-device roster

  • sloth --known-mac MAC / sloth --known-macs FILE — tell sloth which devices you already recognise. The file is one MAC per line with # comments; malformed lines are reported with their line number and skipped, so one typo does not discard a roster of 200 good entries.

    Combined with a network designation above, a device associated to your network that is not on the roster raises UNKNOWN_DEVICE. Both inputs are required, so the check is silent unless you opted into each.

    This is what separates unfamiliar from intrinsically odd. Without a roster, sloth can only score a device on properties — randomised MAC, unknown vendor, no hostname — and with randomisation default on every handset, that fires on your own staff.

    Roster reliability: per-probe MAC randomisation rotates constantly, so a roster cannot follow a device that is only probing. Per-SSID randomisation used for association is stable — iOS and Android derive one MAC per network and keep it across reconnects — so a device rostered while on the network keeps that address. Roster associated devices; use the presence classification in [7] Probe for passers-by.

    sudo ./sloth --hop --my-ssid CorpWiFi --known-macs /etc/sloth/roster.txt

WiFi SIGINT usage

Sloth is fully passive — it never injects probe requests, never sends deauth frames, never associates with anything. All wireless data is sniffed by a monitor-mode interface that's been put into monitor mode by an external tool (iw, airmon-ng, etc.) before sloth starts.

# 1. Set an adapter to monitor mode (external — sloth never touches link state).
sudo ip link set wlan1 down
sudo iw dev wlan1 set type monitor
sudo ip link set wlan1 up

# 2. Run sloth. It auto-discovers the monitor iface; --eapol-dir
#    streams captured PMKIDs + 4-way handshakes to hashcat format.
sudo ./sloth --eapol-dir /tmp/sloth-eapol -o /tmp/sloth.jsonl

# 3. While sloth runs:
#    [k] PNL    — devices and the SSIDs they're probing for
#    [b] Beacons — APs + cipher / AKM / MFP inventory + hidden-SSID reveal
#    [e] EAPOL  — captured handshakes (PMKID = single-frame crack)
#    [j] Seqnum — randomised MACs correlated to the same physical radio
#    [7] Probe  — raw probe-request feed (Roaming clients)

# 4a. Offline crack against the streamed handshake / PMKID file.
hashcat -m 22000 /tmp/sloth-eapol/eapol.22000 rockyou.txt

# 4b. OR replay an individual handshake through aircrack-ng.
aircrack-ng -w rockyou.txt -e "SSID" \
    /tmp/sloth-eapol/<bssid>_<sta>.pcap

Build

make                          # full build (ncurses + pcap + nl80211)
make WITH_PCAP=0              # no capture, no probe view
make WITH_NCURSES=0           # headless / embedded
make embedded                 # shortcut: no ncurses, no pcap
make test                     # 2122 assertions (no root, no terminal, no network)
make mutate                   # mutation-test the suite itself (verify the verifier)

Requires libpcap-dev and libncursesw-dev for the full build. The test build needs neither. The mutate target is opt-in and offline — see docs/wiki/mutation-testing.md.

Keybindings

Use [?] inside sloth for an up-to-date reference card.

View selection

Key View Key View Key View
1 Interfaces d DHCP u QUIC
2 Connections s SSDP r DNS
3 WiFi b Beacons p NTP
4 Packets a Deauth i ICMP
5 Processes h HTTP v Alerts
6 Stats t TLS g Devices
7 Probe ? Help o Dash
8 ARP k PNL e EAPOL
9 mDNS j Seqnum w Assoc
0 NBNS m Channel l OSI stack
x Twins
y KARMA
z Rogue RADIUS

Global

Key Action
Tab Cycle views forward
n Toggle DNS hostname resolution (in conn/proc/stats views)
/ Filter current log view — type to refine, Enter to commit, Esc to cancel
\ Clear filter
q/Q Quit

Per-view

Key Action
/ Navigate rows
c Clear the current log view's ring buffer
t (Interfaces) Toggle iface visibility
Enter (Interfaces/Packets) Open detail panel
f (Conns/Packets) Cycle filter
s (Conns) Cycle sort

Alerts

Six rules feed VIEW_ALERTS. New keys also append to the JSONL stream and (if --pcap-dir is set) trigger a per-alert pcap dump.

Rule Severity Trigger match_ip / port
PORT_SCAN CRIT scanner's IP touched ≥ 8 distinct local ports scanner IP / 0
DEAUTH_FLOOD WARN ≥ 5 deauth/disassoc frames in 5 s to one target — (L2 only)
NXDOMAIN_BURST WARN ≥ 10 NXDOMAIN replies to one src in 60 s src / 53
THREAT_DOMAIN CRIT DNS qname matches embedded IOC list src / 53
THREAT_IP CRIT conn remote IP matches embedded IOC list remote IP / port
BEACONING WARN flow with ≥ 5 samples, mean ≥ 10 s, jitter/mean ≤ 0.25 remote IP / port

The IOC lists in src/threat_intel.c are intentionally synthetic (RFC 5737 doc IPs, .testing / .example sentinel domains). They exist so the alerts pipeline can be exercised in tests — replace them with your own feed for production use.

JSONL schema

Each line is one JSON object. ts is a Unix timestamp; strings are RFC 8259 escaped.

{"type":"dns","ts":1700000000,"src":"192.168.1.5","qname":"example.com","qtype":"A","answer":"93.184.216.34","is_resp":1}
{"type":"tls","ts":1700000001,"src":"10.0.0.5","dst":"93.184.216.34","host":"example.com","ver":"TLS 1.3","ja3":"deadbeefcafef00d00112233445566ff"}
{"type":"quic","ts":1700000002,"src":"10.0.0.5","dst":"1.1.1.1","host":"cloudflare.com","ver":"v1"}
{"type":"http","ts":1700000003,"src":"10.0.0.5","host":"example.com","method":"GET","path":"/index.html"}
{"type":"ntp","ts":1700000004,"src":"10.0.0.1","dst":"192.168.1.5","mode":"server","version":4,"stratum":1,"ref":"GPS"}
{"type":"icmp","ts":1700000005,"src":"192.168.1.5","dst":"8.8.8.8","desc":"Echo Req","ty":8,"code":0,"seq":42,"v6":0}
{"type":"alert","ts":1700000006,"title":"THREAT_DOMAIN","detail":"192.168.1.5 queried malware.testing.com (IOC malware.testing.com)","key":"threat-d:malware.testing.com","sev":2,"ty":3,"count":1}

Streaming and SIEM forwarding

Sloth's JSONL stream is the contract for any downstream consumer. Two runnable reference programs ship in examples/ — stdlib- only Python 3, no pip install step — so the schema has a companion you can hand to a SIEM team in five minutes.

Reference consumer

examples/consumer/sloth-stream.py — connects to the data socket, parses every record type from the schema, supports filtering and pretty-print. Read it as the textbook consumer loop (connect → stream → json.loads → filter → format → reconnect); the same shape ports directly to Go (bufio.Scanner), Node (readline), or Swift's Network.framework.

# Tail every record sloth emits, with ANSI colour
python3 examples/consumer/sloth-stream.py unix:/tmp/sloth.sock

# Only alerts, from any source
python3 examples/consumer/sloth-stream.py tcp:127.0.0.1:8765 --type alert

# Raw JSON pass-through into jq
python3 examples/consumer/sloth-stream.py unix:/tmp/sloth.sock --raw | jq .

# 5-second rolling tally by record type
python3 examples/consumer/sloth-stream.py unix:/tmp/sloth.sock --count

Reference SIEM forwarder

examples/forwarder/sloth-forward.py — reads from the data socket, batches, and pushes to a downstream SIEM. Three sinks ship:

Sink Wire format
hec Splunk HTTP Event Collector (JSON envelopes over HTTPS POST)
syslog RFC 5424 over UDP or TCP
elastic Elasticsearch / OpenSearch Bulk API (NDJSON to /_bulk, time-rolled indices via strftime patterns, basic auth or API key)
# Splunk HEC
python3 examples/forwarder/sloth-forward.py unix:/tmp/sloth.sock \
    --sink hec \
    --hec-url       https://splunk.example.com:8088/services/collector \
    --hec-token-env SLOTH_HEC_TOKEN

# RFC 5424 syslog (UDP)
python3 examples/forwarder/sloth-forward.py unix:/tmp/sloth.sock \
    --sink syslog --syslog-host siem.example.com --syslog-port 514

# Elasticsearch with daily-rolled indices
python3 examples/forwarder/sloth-forward.py unix:/tmp/sloth.sock \
    --sink elastic \
    --es-url       https://elastic.example.com:9200 \
    --es-index     'sloth-events-%Y.%m.%d' \
    --es-api-key-env SLOTH_ES_API_KEY

Batching (--batch-size and --batch-ms), exponential-backoff retries (--max-retries, --retry-backoff), and 30-second stderr stats (received=N forwarded=N dropped=N retries=N) are built in.

Adding a sink is ~30 lines — the sink interface is two members (.name, .send(batch)); the retry loop, batching, filtering, and reconnect logic all stay in main(). See examples/forwarder/README.md for the "how to add a sink" recipe and production patterns (systemd unit with EnvironmentFile, "one forwarder per sink", when to combine with -o FILE for durability).

Delivery semantics: the forwarder mirrors sloth's non-durable contract (MISSION.md §4). After --max-retries, batches drop. If you need durability, also pass -o FILE to sloth and ship the file via a separate log-shipping agent (filebeat, vector, fluent-bit) — that gives you durability, this gives you low-latency triage.

Architecture

The codebase is built around a platform vtable (platform_ops_t in include/sloth.h):

typedef struct {
    int  (*get_ifaces)(iface_stat_t *out, int max);
    int  (*get_conns)(conn_t *out, int max);
    int  (*wifi_scan)(wifi_ap_t *out, int max);
    int  (*get_wifi_stations)(wifi_sta_t *out, int max);
    int  (*get_arp)(arp_entry_t *out, int max);
    int  (*get_dhcp)(dhcp_lease_t *out, int max);
    void (*init)(void);
    void (*cleanup)(void);
} platform_ops_t;

Adding a new data source means adding one function pointer here and implementing it in each backend (src/platform/linux.c, bsd.c, stub.c, win32.c) and the test fake (tests/fake_platform.c). Views read from sloth_state_t; they never call platform ops directly.

Data flow

   capture thread                main loop (poll_data, 1 Hz)
   ──────────────                ───────────────────────────
   pcap_loop()                   g_platform.get_ifaces()
       │                         g_platform.get_conns()
       ▼                         g_platform.get_arp()
   dns_log_parse / record        … other platform reads
   tls_log_parse / record               │
   quic_log_parse / record              ▼
   http_log_parse / record       *_snapshot()   ◄── ring buffer copy
   ntp_log_parse / record               │
   icmp_log_parse / record              ▼
       │                         alerts_update()
       ├─► jsonl_emit_*()        beacon_update()
       │     (if -o set)         devices_update()
       └─► ring buffer                  │
                                        ▼
                                 tui_draw()

Packet decode runs in a dedicated pcap thread. The eight log modules each have an independent ring buffer behind a pthread_mutex_t; *_snapshot() helpers copy the latest entries into sloth_state_t once per poll. Synthesis modules (alerts, devices, beacon-detect) read snapshot state and produce derived views.

Code layout

include/sloth.h            shared types: state, packet, conn, alert, device, ...
src/main.c                 CLI, signal handlers, main loop
src/tui.c                  ncurses / ANSI rendering, key polling
src/platform/linux*.c      Linux backends (rtnetlink, nl80211, /proc, INET_DIAG)
src/capture/capture.c      libpcap thread; per-protocol parser dispatch
src/{dns,tls,quic,http,
     ntp,icmp,dns,...}_log.c     ring buffers + per-record snapshot
src/{alerts,beacon_detect,
     devices,threat_intel,
     filter,jsonl,alert_pcap}.c  synthesis + export
src/views/*.c              one file per VIEW_*
src/md5.c                  RFC 1321 implementation used for JA3
tests/                     unit tests, fake platform, scenarios

Testing

make test    # 2122 assertions, no root, no terminal, no network

Every real-data path is replaced by a controllable fake:

  • tests/fake_platform.c — implements g_platform with deterministic in-memory data. All vtable functions read from a global fake_net_t.
  • tests/null_tui.c — stubs ncurses functions as no-ops; TPRINT falls back to printf, so view draw functions execute their full rendering logic.
  • tests/scenarios.c — named configurations (empty, idle, busy, many_conns, wifi_crowded, monitor_env) used by render smoke tests.

Protocol parsers (DNS, TLS, JA3, QUIC, HTTP, NTP, ICMP, mDNS, NBNS, DHCP, SSDP) are tested with raw byte arrays constructed by hand from the relevant RFCs. Each test computes byte values from first principles so the tests are not circular.

0x00,0x00, 0x84,0x00,          // DNS hdr: ID=0, QR=1 AA=1
0x00,0x00, 0x00,0x01,          //          QDCOUNT=0, ANCOUNT=1
…

The MD5 implementation used for JA3 is independently validated against all RFC 1321 test vectors (tests/test_md5.c).

Mutation testing (verify the verifier)

make test is the oracle this project trusts. To trust it, sloth runs mutation testing — small faults are introduced into src/*.c, the suite reruns, every mutant should be killed. Surviving mutants are concrete test-suite gaps. The harness lives at .github/scripts/mutate.py, runs via make mutate, and is documented in docs/wiki/mutation-testing.md along with the equivalence-class taxonomy (.github/scripts/mutate-equivalents.txt) that suppresses structurally-untestable noise (function-parameter array sizes, stack buffer sizing literals, loop bounds reading zero-init tail, lookup-table data blocks). Current aggregate across 18 files: 51.0% of considered mutants killed — see the badge at the top.

Status

Code: ~18k lines of C99 across 88 source files. Tests: 2122 assertions plus a make mutate harness. Reference Python consumer + 3-sink SIEM forwarder under examples/. License: see project root.

Sloth was built as a passive monitor. It will not scan, fuzz, attack, or attempt to deauth or de-associate anything. If that's what you need, use a different tool.

About

Passive terminal network monitor for Linux

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages