sip-exporter

module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: AGPL-3.0

README ΒΆ

SIP-exporter

EN | RU

High-performance eBPF-based SIP monitoring service that captures and exports telephony metrics to Prometheus-compatible systems (Prometheus, VictoriaMetrics, etc.). Captures SIP packets directly in the Linux kernel using eBPF, minimizing userspace processing overhead.

Go Test Go Vulncheck Container Scan Go Report Card Docker Pulls GitHub Release License Issues

Table of Contents

Key Features

  • ⚑ Low overhead β€” eBPF packet filtering in kernel space
  • 🐳 Single container deployment β€” no external dependencies
  • πŸ”§ Configurable SIP ports β€” monitor custom ports via environment variables
  • πŸ“ˆ Prometheus native β€” standard /metrics endpoint for scraping
  • 🏷️ Per-carrier metrics β€” CIDR-based carrier resolution for all SIP metrics
  • 🏷️ Per-device-type metrics β€” User-Agent classification for all SIP metrics
  • πŸ“ž Voice quality (RFC 6035) β€” MOS scores, jitter, packet loss from SIP PUBLISH/NOTIFY
  • 🎧 RTP media analysis β€” jitter, packet loss, and MOS (E-model G.107) from RTP streams correlated with SIP dialogs, with no voice payload captured (header-only)

Quick Start

# docker-compose.yml
services:
  sip-exporter:
    image: frzq/sip-exporter:latest
    privileged: true
    network_mode: host
    environment:
      - SIP_EXPORTER_INTERFACE=eth0
      - SIP_EXPORTER_HTTP_PORT=2112
      - SIP_EXPORTER_SIP_PORT=5060
      - SIP_EXPORTER_SIPS_PORT=5061
      # Optional: carrier labels for per-provider metrics
      # - SIP_EXPORTER_CARRIERS_CONFIG=/etc/sip-exporter/carriers.yaml
      # Optional: user-agent labels for per-device-type metrics
      # - SIP_EXPORTER_USER_AGENTS_CONFIG=/etc/sip-exporter/user_agents.yaml
    # volumes:
    #   - ./examples/carriers.yaml:/etc/sip-exporter/carriers.yaml:ro
    #   - ./examples/user_agents.yaml:/etc/sip-exporter/user_agents.yaml:ro
docker compose up -d
curl http://localhost:2112/metrics

Access metrics at http://localhost:2112/metrics.

Core Technology

This service uses eBPF (extended Berkeley Packet Filter) attached to AF_PACKET sockets to intercept SIP packets (UDP/5060-5061) at L4 without overhead of iptables/nftables or userspace daemons like tcpdump. Filtered packets are delivered to userspace via the socket for efficient Go processing.

Architecture

SIP + RTP Traffic β†’ NIC β†’ eBPF socket filter β†’ AF_PACKET socket β†’ Go poller β†’ SIP parser + RTP tracker β†’ Prometheus

Performance

Zero packet loss up to 2,000 CPS (~28,000 PPS) with full SIP dialog lifecycle, at <15% CPU and ~15 MB RAM. GC stop-the-world pauses under 1 ms β€” 400Γ— smaller than socket buffer capacity, ensuring packets are never lost due to GC. Memory is stable under sustained load with no leaks detected.

Go micro-benchmarks:

Operation Latency Memory
Parse BYE packet (L2β†’SIP) ~860 ns 712 B/op
Parse INVITE packet (L2β†’SIP) ~1.1 ΞΌs 808 B/op
Parse 200 OK packet (L2β†’SIP) ~2.0 ΞΌs 1176 B/op

Full load test results: docs/BENCHMARK.md.

Install

docker pull frzq/sip-exporter:latest
Configure

Environment variables:

  • SIP_EXPORTER_INTERFACE - net interface (required)
  • SIP_EXPORTER_HTTP_PORT - http port for prometheus (default 2112)
  • SIP_EXPORTER_LOGGER_LEVEL - log level (default info)
  • SIP_EXPORTER_SIP_PORT - SIP port (default 5060)
  • SIP_EXPORTER_SIPS_PORT - SIPS port (default 5061)
  • SIP_EXPORTER_OBJECT_FILE_PATH - path to eBPF object file (default /usr/local/bin/sip.o)
  • SIP_EXPORTER_CARRIERS_CONFIG - path to carriers YAML config (optional, see examples/carriers.yaml)
  • SIP_EXPORTER_USER_AGENTS_CONFIG - path to user-agents YAML config (optional, see examples/user_agents.yaml)
  • SIP_EXPORTER_RTP_CAPTURE - enable RTP media capture and analysis (default true)
  • SIP_EXPORTER_RTP_STREAM_TTL - idle RTP stream expiry, RFC 3550 Β§6.3.5 timeout (default 30s)
  • SIP_EXPORTER_IGNORE_OUTGOING - ignore outgoing packets, count incoming only (default false)
  • SIP_EXPORTER_TELEMETRY - anonymous usage telemetry, opt-out with false (default true)

The container must run with --privileged and --network host (eBPF requires CAP_BPF and access to the network interface). See Security for details on why this is safe.

Metrics

All metrics are exposed at /metrics in Prometheus exposition format. All SIP metrics include carrier and ua_type labels for multi-dimensional analysis. The exporter provides:

  • Traffic counters β€” SIP request types (INVITE, BYE, REGISTER, etc.) and response status codes (100–606)
  • Active sessions β€” real-time count of active SIP dialogs
  • RFC 6076 performance metrics β€” SER, SEER, ISA, SCR, ASR, NER, RRD, SPD, TTR, PDD
  • RFC 6035 voice quality metrics β€” NLR, JDR, BLD, GLD, RTD, ESD, IAJ, MAJ, MOSLQ, MOSCQ, RLQ, RCQ, RERL
  • RTP media metrics β€” rtp_packets_total, rtp_packets_lost_total, rtp_jitter_milliseconds, rtp_mos_score, rtp_active_streams (labels: carrier,ua_type,codec)
  • Extended metrics β€” ISS, SDC, ORD, LRD

Full reference with formulas, examples, and RFC section mapping: docs/METRICS.md

Per-Carrier Metrics

If your SIP infrastructure handles traffic from multiple operators (telecom providers, SIP trunks, PBX clusters), you need to see metrics per operator, not in aggregate.

The carrier feature solves this by mapping IP subnets to operator names. Every metric β€” INVITE count, SER, active sessions, RRD latency β€” gets a carrier label, so you can build separate Grafana dashboards and alerts for each operator.

How it works:

The exporter looks at the source IP of every SIP request and matches it against CIDR subnets in a YAML config. When UAC at 10.1.5.20 sends an INVITE, the exporter finds that 10.1.5.20 falls within 10.1.0.0/16 defined for carrier "telecom-alpha", and tags all metrics for this call β€” the INVITE itself, the 200 OK response, the BYE, even the dialog expiry β€” with carrier="telecom-alpha".

This means:

  • INVITE from 10.1.5.20 β†’ metrics labeled carrier="telecom-alpha"
  • INVITE from 192.168.11.3 β†’ metrics labeled carrier="telecom-beta"
  • INVITE from 8.8.8.8 (not in any subnet) β†’ metrics labeled carrier="other"

Setup:

# docker-compose.yml
services:
  sip-exporter:
    image: frzq/sip-exporter:latest
    privileged: true
    network_mode: host
    environment:
      - SIP_EXPORTER_INTERFACE=eth0
      - SIP_EXPORTER_CARRIERS_CONFIG=/etc/sip-exporter/carriers.yaml
    volumes:
      - ./examples/carriers.yaml:/etc/sip-exporter/carriers.yaml:ro
# carriers.yaml β€” map your operators' IP subnets
carriers:
  - name: "telecom-alpha"
    cidrs:
      - "10.1.0.0/16"
  - name: "telecom-beta"
    cidrs:
      - "192.168.10.0/24"
      - "192.168.11.0/24"

After that, metrics look like:

sip_exporter_invite_total{carrier="telecom-alpha",ua_type="other"}  1523
sip_exporter_ser{carrier="telecom-alpha",ua_type="other"}            95.2
sip_exporter_ser{carrier="telecom-beta",ua_type="other"}             87.4
sip_exporter_ser{carrier="other",ua_type="other"}                     0.0

Things to know:

  • Carrier is determined at request time (INVITE/REGISTER/OPTIONS), not response time. If carrier-A sends INVITE and carrier-B answers 200 OK, all metrics still go to carrier-A β€” the operator who initiated the call
  • If source IP doesn't match any CIDR, destination IP is tried. If neither matches β†’ carrier="other"
  • When CIDRs overlap, first match wins β€” list specific subnets before broad ones
  • Without the config file, all metrics get carrier="other" β€” nothing breaks
  • Each carrier can have multiple CIDRs, and multiple carriers can be defined
  • CIDR notation is required β€” plain IPs without / are rejected. Use /32 for a single host, e.g. "10.226.97.5/32" instead of "10.226.97.5"

Full config reference with examples: examples/carriers.yaml

Per-Device-Type Metrics (User-Agent Classification)

If you need to see metrics per SIP device type β€” IP phones vs softphones vs SBCs β€” the User-Agent classification feature adds a ua_type label to every metric.

The exporter reads the User-Agent SIP header from each request and matches it against regex patterns in a YAML config. Every metric β€” INVITE count, SER, active sessions, SPD duration β€” gets a ua_type label, so you can build separate Grafana dashboards and alerts for each device family.

How it works:

The exporter parses the User-Agent header of every SIP request and matches it against regex patterns in a YAML config. When a phone with User-Agent: Yealink SIP-T46S 66.15.0.10 sends an INVITE, the exporter matches ^Yealink and tags all metrics for this call with ua_type="yealink".

This means:

  • INVITE from Yealink phone β†’ metrics labeled ua_type="yealink"
  • INVITE from Grandstream phone β†’ metrics labeled ua_type="grandstream"
  • INVITE with unknown User-Agent β†’ metrics labeled ua_type="other"

Setup:

# docker-compose.yml
services:
  sip-exporter:
    image: frzq/sip-exporter:latest
    privileged: true
    network_mode: host
    environment:
      - SIP_EXPORTER_INTERFACE=eth0
      - SIP_EXPORTER_USER_AGENTS_CONFIG=/etc/sip-exporter/user_agents.yaml
    volumes:
      - ./user_agents.yaml:/etc/sip-exporter/user_agents.yaml:ro
# user_agents.yaml β€” map User-Agent patterns to device types
user_agents:
  - regex: '(?i)^Yealink'
    label: yealink
  - regex: '(?i)^Grandstream'
    label: grandstream
  - regex: '(?i)^Cisco/SPA'
    label: cisco_spa
  - regex: '(?i)^Kamailio'
    label: kamailio
  - regex: '(?i)^Asterisk'
    label: asterisk

After that, metrics look like:

sip_exporter_invite_total{carrier="telecom-alpha",ua_type="yealink"}     1523
sip_exporter_ser{carrier="telecom-alpha",ua_type="yealink"}               95.2
sip_exporter_ser{carrier="telecom-alpha",ua_type="grandstream"}           87.4
sip_exporter_ser{carrier="telecom-alpha",ua_type="other"}                  0.0

Things to know:

  • UA type is determined at request time (INVITE/REGISTER/OPTIONS), using the same tracker mechanism as carrier. Responses inherit ua_type from the request tracker, not from the response's own headers
  • The User-Agent header is extracted from all SIP packets, but SIP responses typically use the Server header, so in practice only requests provide meaningful classification
  • If no pattern matches β†’ ua_type="other"
  • When patterns overlap, first match wins β€” list specific patterns before broad ones
  • Without the config file, all metrics get ua_type="other" β€” nothing breaks
  • Patterns are case-insensitive when using (?i) prefix
  • Works together with carrier β€” every metric has both carrier and ua_type labels for two-dimensional analysis

Combined carrier + ua_type queries:

# SER for Yealink phones on a specific carrier
sip_exporter_ser{carrier="telecom-alpha",ua_type="yealink"}

# Active sessions by device type (across all carriers)
sum by (ua_type) (sip_exporter_sessions)

# INVITE rate per carrier per device type
sum by (carrier, ua_type) (rate(sip_exporter_invite_total[5m]))

Full config reference with examples: examples/user_agents.yaml

RTP Media Analysis

In addition to SIP signaling, the exporter can capture and analyze RTP media streams to measure real call quality (jitter, packet loss, MOS). RTP streams are correlated with SIP dialogs: when a 200 OK to INVITE carries SDP, the exporter registers the negotiated media endpoints and tracks the matching RTP flows until BYE (or Session-Expires expiry). This means RTP metrics inherit the dialog's carrier, ua_type, and the negotiated codec labels.

Metrics produced:

Metric Type Description
sip_exporter_rtp_packets_total counter RTP packets observed
sip_exporter_rtp_packets_lost_total counter packets lost (RFC 3550 sequence-gap accounting)
sip_exporter_rtp_jitter_milliseconds histogram interarrival jitter (RFC 3550 A.8)
sip_exporter_rtp_mos_score histogram MOS-LQ via ITU-T G.107 E-model (1.0–4.5)
sip_exporter_rtp_active_streams gauge active RTP streams correlated with dialogs

Privacy: only the 12-byte RTP header is captured β€” voice payload is truncated in the kernel (eBPF) before reaching userspace, so no call audio is inspected or stored.

RTP capture is on by default and can be disabled with SIP_EXPORTER_RTP_CAPTURE=false (the eBPF filter then drops RTP at the kernel level). Note: RTP without a correlated SIP dialog (no SDP exchange seen) is dropped, so only media for monitored calls is counted.

# Average MOS over the last 5m (per codec)
sum by (codec) (rate(sip_exporter_rtp_mos_score_sum[5m]))
  / sum by (codec) (rate(sip_exporter_rtp_mos_score_count[5m]))

# Packet loss ratio by carrier
sum by (carrier) (rate(sip_exporter_rtp_packets_lost_total[5m]))
  / sum by (carrier) (rate(sip_exporter_rtp_packets_total[5m]))

See docs/METRICS.md for the full RTP reference, formulas, and label resolution.

Development

Requirements
  • Go 1.25+
  • Clang/LLVM (for eBPF compilation)
  • Linux kernel with eBPF support
  • Root privileges (required for eBPF and packet socket)
Test Coverage
Package Coverage
internal/config 100.0%
pkg/log 95.5%
internal/server 90.5%
internal/service 75.4%
internal/exporter 64.0%

Test suite:

  • Unit tests β€” MC/DC standard, all business logic covered
  • 120 E2E tests β€” real SIP traffic via SIPp + testcontainers-go, validates all RFC 6076, RFC 6035, and RTP metrics
  • 13 load tests β€” PPS throughput, VQ reports, concurrent sessions, memory stability, GC pauses, scrape latency

Benchmark

Load testing results: 0% packet loss at 2,000 CPS (28,000 PPS).

See BENCHMARK.md for detailed results, methodology, and optimization notes.

Integration

Alerting

Pre-configured alerting examples are available in ALERTING.md:

  • Prometheus alert rules β€” Critical, warning, and info alerts for SER, ISA, RRD, and more
  • Grafana dashboard β€” Ready-to-import JSON with carrier-filtered panels
  • Alertmanager examples β€” Slack, PagerDuty, and Email integrations
  • Best practices β€” Scrape intervals, retention, threshold tuning
Grafana Dashboard

Import the pre-built dashboard into your Grafana instance:

  1. Open Grafana β†’ Dashboards β†’ Import
  2. Upload examples/grafana-dashboard.json or copy the JSON content
  3. Select your Prometheus or VictoriaMetrics datasource

The dashboard includes all available metrics: traffic counters, SIP request/response breakdowns, active sessions, RFC 6076 performance metrics (SER, SEER, ISA, SCR, NER), RTP media analysis (active streams, packet rate, loss rate, MOS, jitter by codec), voice quality metrics (RFC 6035: MOS, jitter, packet loss), delay histograms (RRD, TTR, PDD, SPD, ORD, LRD), session quality metrics (ISS, ASR, SDC), and system errors.

Dashboard file: examples/grafana-dashboard.json

Metrics Storage Compatibility

SIP-Exporter exports metrics in Prometheus exposition format, compatible with:

  • Prometheus β€” pull-based monitoring
  • VictoriaMetrics β€” high-performance time-series database
  • Grafana Cloud β€” cloud-based observability
  • Any Prometheus-compatible scraper β€” the /metrics endpoint follows the standard format

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

See LICENSE for full text.

Commercial Use
  • βœ… Free for personal and educational use
  • βœ… Free for commercial use with conditions
  • ⚠️ If you modify and run as a public service, you must open-source your modifications
  • πŸ“§ For commercial licensing without AGPL requirements, contact the author

Changelog

See CHANGELOG.md for version history.

Directories ΒΆ

Path Synopsis
internal
dto
rtp
sdp
Package sdp parses SDP (RFC 4566) media descriptions from SIP message bodies for RTP stream correlation.
Package sdp parses SDP (RFC 4566) media descriptions from SIP message bodies for RTP stream correlation.
ua
vq
pkg
log

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL