Merging or correlating multiple call legs
This guide explains how VoIPmonitor identifies and links multiple call legs belonging to the same end-to-end call.
Overview
| Scenario | Description | VoIPmonitor Behavior |
|---|---|---|
| Same Call-ID | Multiple legs share identical SIP Call-ID | Automatically treated as one CDR (no configuration needed) |
| Different Call-IDs | SBC/B2BUA generates new Call-ID for each leg | Creates separate CDRs (requires correlation configuration) |
Same Call-ID Behavior
When multiple legs share the same SIP Call-ID, VoIPmonitor automatically treats them as a single call:
- Result: One CDR in the GUI, all packets in one PCAP file
- BYE handling: When a BYE is received, VoIPmonitor finalizes the CDR for that Call-ID
- No configuration needed: This is automatic and cannot be disabled
ℹ️ Note: If you want separate CDRs per leg, configure your PBX/SBC to generate unique Call-IDs, then use the correlation methods below.
Correlation Methods for Different Call-IDs
When calls pass through an SBC/B2BUA that generates new Call-IDs, choose one of these methods:
| Method | Result | Use Case |
|---|---|---|
| Linking | Separate CDRs, visually linked in GUI | View each leg independently with navigation |
| Merging | Single CDR and PCAP file | Treat entire call as one record |
Method 1: Linking Separate CDRs
Keeps legs as distinct database entries but associates them visually.
Automatic Linking (Legs by CID)
Built-in, no configuration required:
- Matches CDRs where last 6 digits of caller/called numbers match within ±10 seconds
- Found in CDR detail view → Legs by CID tab
- Use Merge button to combine linked legs into unified SIP history view
Custom Header Linking
Link legs using a shared SIP header. Results appear in Legs by header tab.
GUI Configuration (no restart required):
- Navigate to Settings → Custom Headers → CDR
- Add the header name (e.g.,
X-Correlation-ID) - Enable "match in SIP by header" checkbox
voipmonitor.conf (requires restart):
# Store header in cdr_next.match_header for leg matching
matchheader = X-Correlation-ID
# Alternative Call-ID for correlation when standard Call-ID changes
call_id_alternative = X-Correlation-ID
Method 2: Merging into Single CDR
Combines multiple legs into one CDR and one PCAP file. Requires the second leg's INVITE to contain a header with the first leg's Call-ID.
Configuration (voipmonitor.conf):
# Header containing parent Call-ID in outbound INVITEs
callidmerge_header = X-PARENT-CALLID
# Optional: Encrypt header value
#callidmerge_secret = yourSecretString
PBX Configuration Example (Asterisk):
[macro-add-parent-callid]
exten => s,1,NoOp(Adding Parent-Call-ID header)
exten => s,n,Set(ORIG_CALLID=${SIPHEADER(Call-ID)})
exten => s,n,SipAddHeader(X-PARENT-CALLID: ${ORIG_CALLID})
exten => s,n,MacroExit()Comparison: Linking vs Merging
| Feature | Linking (matchheader) | Merging (callidmerge_header) |
|---|---|---|
| Database entries | Separate CDRs | Single CDR |
| PCAP files | Separate files | Single merged file |
| GUI navigation | "Legs by header" tab | Single call view |
| PBX requirement | Add correlation header | Add header with parent Call-ID |
| Restart required | GUI: No / Config: Yes | Yes |
MOS Analysis in Multi-Leg Calls
Default Behavior
When multiple legs are merged (same Call-ID), the GUI displays MOS from the longest RTP stream only. This can hide quality issues on shorter legs.
Solution 1: call_branches (Experimental)
Evaluate each leg separately:
call_branches = yes
⚠️ Warning: The call_branches option is experimental. Test before deploying to production.
Solution 2: Separate CDRs per Leg
Generate unique Call-IDs for each leg to create separate CDRs:
- Each CDR shows exact packet loss/MOS for that specific leg
- Link related CDRs using
matchheaderfor navigation - Best for detailed per-stream quality analysis
| Approach | MOS Visibility | Complexity |
|---|---|---|
| Default merged CDR | Only longest stream | Low |
| call_branches = yes | Per-branch evaluation | Low (experimental) |
| Separate CDRs + linking | Individual MOS per leg | High (requires PBX changes) |
Transferred Calls: Preserving Audio
When calls are transferred (REFER/re-INVITE), BYE may terminate the original leg while transfer continues. To preserve audio:
# Continue recording RTP after BYE
ignore_rtp_after_bye_confirmed = no
# Continue duration counting after BYE
ignore_duration_after_bye_confirmed = no
💡 Tip: If audio for transferred portion is missing after configuring correlation, add these options and restart the sniffer.
IP-Based Access Control
When using filter_ip user restrictions, unique Call-IDs per leg are required for proper security.
- Problem
- If legs share the same Call-ID, users can download PCAPs containing traffic from IPs outside their permission (all leg traffic is combined).
- Solution
- Configure PBX/SBC to generate unique Call-IDs per network leg segment. Each CDR then contains only traffic from specific IPs.
⚠️ Warning: Do NOT use callidmerge_header when IP-based access control is required. Merging defeats the security purpose.
See User_Management for configuring IP-based restrictions.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| One CDR instead of two | Legs share same Call-ID | Expected behavior; configure unique Call-IDs if separate CDRs needed |
| Audio missing after transfer | BYE terminates recording | Set ignore_rtp_after_bye_confirmed = no
|
| Correlation not working | Header mismatch or not present | Verify header in PCAP; check exact spelling; restart sniffer for config changes |
| Capture stops on BYE | Same Call-ID = same session | For independent tracking, use unique Call-IDs per leg |
See Also
AI Summary for RAG
Summary: VoIPmonitor call correlation handles multi-leg calls in two scenarios: (1) Same Call-ID legs are automatically treated as one CDR; (2) Different Call-IDs (from SBC/B2BUA) require configuration. Two methods exist: Linking (matchheader) keeps separate CDRs visually associated via "Legs by header" tab; Merging (callidmerge_header) combines legs into single CDR where second INVITE contains parent Call-ID header. For MOS visibility in merged calls, default shows only longest stream; use call_branches=yes (experimental) or separate CDRs for per-leg analysis. Transferred calls may lose audio if BYE terminates recording; fix with ignore_rtp_after_bye_confirmed=no. For IP-based access control (filter_ip), unique Call-IDs per leg are required to prevent unauthorized PCAP access.
Keywords: call correlation, leg matching, Call-ID, matchheader, call_id_alternative, callidmerge_header, ignore_rtp_after_bye_confirmed, call_branches, MOS, multi-leg call, Legs by CID, Legs by header, B2BUA, SBC, CDR merging, BYE termination, call transfer, filter_ip, IP restrictions, packet loss per stream
Key Questions:
- Why do I see only one CDR when my call has multiple legs?
- How to link call legs with different Call-IDs?
- What is the difference between matchheader and callidmerge_header?
- How to merge multiple call legs into a single CDR?
- Why is MOS only showing for the longest RTP stream?
- How to see MOS for individual legs in a merged CDR?
- Why is audio missing for transferred calls?
- How to preserve audio after BYE during call transfer?
- Why can users with IP restrictions download PCAPs with unpermitted traffic?
- How to view exact packet loss per RTP stream?