sipmon
A passive SIP/RTP signaling and media quality monitoring tool. A standalone Rust executable deployed on a mirrored port / packet capture box, with no dependency on a running PBX. Inputs may be a live capture, a pcap file, a stdin stream, or a previously recorded event log; output is a live TUI monitor plus exportable analysis results (JSONL).


Features
- Inputs: live interface (libpcap + BPF), offline pcap/pcapng, stdin
tcpdump -w -stream, event-log replay - Modes:
liveinteractive monitoring /recordrecording with live TUI (--headless= no UI,-ddaemonizable) /replay - SIP correlation: Call-ID call state machine, transactions, Call-ID / number / IP / SSRC indexes, SIP-over-TCP reassembly
- Media quality: RFC3550 jitter/loss, RTCP RR RTT, one-way delay estimate, E-model MOS
- TURN detection: auto-learns TURN servers, labels
turn-client/turn-peerrelay legs - Diagnostics: 20+ rules for Contact reachability, Record-Route, SDP/RTP consistency, one-way media, TURN allocation/refresh
- TUI: Overview / Search / Call Detail / Heatmap / Streams / Event Log / IP Stats pages
- Analysis: PDD/setup/ring timing, hangup initiator (BYE, CANCEL, reject), per-IP loss over 1s…1h windows
- Export: JSONL on exit or via
export;queryfetches a Call-ID flow for scripting
Quick start
|
Commands
| Command | Description |
|---|---|
(none) |
Default mode: positional FILE dispatched by extension (.pcap/.pcapng → file, .evlog → replay, .jsonl → snapshot view); no FILE starts a live capture. --no-tui for headless output |
live |
Live capture + TUI. -i interface, -f BPF filter, --no-media disables RTP/RTCP analysis, -w also writes an event log |
record |
Live capture → event log (-w required). Live TUI on a tty; --headless disables it. -d daemonizes, --pidfile/--logfile for daemon runs. Flushes gracefully on SIGTERM/SIGINT |
- |
Read a pcap byte stream from stdin |
file |
Offline pcap/pcapng. --rate 1x replay speed multiplier, --no-tui, --print-events |
replay |
Replay an event log (TUI / --no-tui) |
query |
No TUI; exports flow + stream stats + RTT + diagnostics for a Call-ID (script friendly) |
export |
Rebuild a snapshot from an event log → JSONL, with --from/--to time filtering |
Common options
--dry-run In-memory analysis only, writes no files
--max-calls N Max calls retained in memory (default 100000)
--max-streams N RTP stream ring cap (default 50000)
--max-diagnostics N Diagnostics ring cap (default 50000)
--diag-level X info|warn|critical (default warn)
--turn-servers IP,… TURN server IP list (auto-learning also supported)
--local-ips IP,… Local (monitored) machine IPs: Call Detail flow/media pin
the local endpoint to the right with ingress/egress arrows
(default: this host's own interface addresses)
--raw-truncate N Truncate stored raw SIP messages to N bytes
--bucket 15m|1h|1d Heatmap bucket granularity (default 15m)
-w/--evlog PATH Write the binary event log to PATH
--export-jsonl PATH Export JSONL on exit
TUI
Pages: Overview 1 · Search 2// · Call Detail 3 · Heatmap 4 · Streams 5 · Event Log 6 · IP Stats 7. Tab/Shift-Tab cycles pages, Space pauses, e exports JSONL, x clears in-memory stats, q/Esc/Ctrl-C quits.
Call Detail uses a fixed four-pane layout: Flow (chronological SIP messages, ↑/↓ selects) · Raw (full bytes of the selected message, PgUp/PgDn scrolls) · Diagnostics · Network (traffic totals + per-stream media table). With a known local IP (--local-ips) the local endpoint is pinned to the right and arrows show the direction — remote -> local is inbound, remote <- local outbound.
IP Stats aggregates per-IP conditions, split by direction (TX = sent by the IP, RX = received): c collapses to a loss-only summary, w cycles the time window, s sorts, Enter drills down to the calls involving an IP.
Event log format
Private binary append-only format. Header holds the SMON magic, version, and timezone; records are ts_delta | ev_type | len | payload. Event types: 1 SipMsgEvt, 2 TxnEvt, 3 CallEvt, 4 StreamSnapEvt (every 5s), 5 RtcpRttEvt, 6 HealthBucketEvt, 7 ErrorEvt, 8 DiagEvt.
No raw RTP payload is stored — only the summaries needed to rebuild the analysis, plus truncated raw SIP messages (--raw-truncate controls the cap). record always writes to disk; live needs an explicit -w.
Diagnostic codes
| Code | Meaning |
|---|---|
CONTACT_UNREACHABLE / CONTACT_PRIVATE_NAT / CONTACT_MCAST |
Contact address unreachable, private NAT, or multicast |
RR_NOT_HONORED / RR_DEPTH_MISMATCH |
Record-Route not honored / depth mismatch |
SDP_HOLD |
SDP carries hold (sendonly/inactive) |
RTP_PT_MISMATCH / RTP_PT_CHANGED / RTP_FLOW_UNEXPECTED |
Payload type mismatch / changed mid-call / RTP flow disagrees with SDP |
ONE_WAY_MEDIA |
One-way media (only receiving, not sending) |
TURN_ALLOC_OK / TURN_ALLOC_FAILED / TURN_REFRESH_FAILED |
TURN allocation succeeded / failed / refresh failed |
TURN_RELAY_MEDIA / TURN_CHANNEL_MEDIA / TURN_SEND_IND_MEDIA |
Media relayed via TURN Relay / ChannelData / Send-Ind |
TURN_LEG_IMBALANCE |
TURN leg packet imbalance (suspected one-way) |
Metric definitions
- RTT:
RTT = arrival_NTP − LSR − DLSRfrom the RTCP RR - One-way delay: RTCP SR NTP↔RTP mapping (when both directions are visible); otherwise an indirect estimate from RTP arrival intervals (labeled "estimate")
- jitter/loss: RFC3550, 64-packet reorder window
- MOS: simplified E-model (G.107):
R = 93.2 − Id − Ie, labeled "estimate"
Limitations
- TLS/SRTP encrypted payloads cannot be parsed; capture at the decryption point
- Absolute one-way delay under one-way passive observation is an estimate; RTCP RTT is the primary metric
- Capturing without interface permissions requires root / elevated privileges (same as tcpdump)
Tests