Database structure: Difference between revisions

From VoIPmonitor.org
(Created page with "= cdr = *ID - unique primary key *id_sensor - match id_sensor in voipmonitor.conf or NULL if not set *calldate - start of the call (counted from first INVITE *callend - end of...")
 
(VS-1789: Document nth_occurrence column in cdr_custom_headers table)
 
(29 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= cdr =
{{DISPLAYTITLE:CDR Database Table Schema}}
*ID - unique primary key
*id_sensor - match id_sensor in voipmonitor.conf or NULL if not set
*calldate - start of the call (counted from first INVITE
*callend - end of the call (counted from the last seen packet)
*duration - callend - calldate in seconds
*connect_duration - callend - time of 200 OK
*progress_time - lenght of ringing
*first_rtp_time - number of seconds since beginning of the call to first rtp packet
*caller - number part of the caller from "From" sip header
*caller_domain - domain part from "From" sip header
*caller_reverse - reversed caller column used to do index searching %search
*callername - number part of the caller from "From" sip header
*callername_reverse - reversed callername used for index searching %search
*called - number part of the called from "To" sip header
*called_domain - domain part from "To" sip header
*called_reverse - reversed called column
*sipcallerip - source IP address from the first INVITE. Use INET_NTOA() to get string
*sipcalledip - destination IP address from the last INVITE.
*whohanged - indication who sent the BYE first
*bye - indication if the call was successfully terminated with BYE - ACK
*lastSIPresponse_id - last SIP response reference to table cdr_sip_response
*lastSIPresponseNum - last SIP response number
*sighup - indication if the call was written during HUP signal sent to sniffer (incomplete CDR)
*dscp - dscp bits


columns starting with a_ represents information for caller RTP stream (RTP sent from caller). Columns starting with b_ represents informatino for called RTP stream (RTP sent from called)
'''Database schema reference for VoIPmonitor. The <code>cdr</code> table stores one record per call; related tables store RTP statistics, custom headers, and metadata.'''


*ab_index - index of RTP stream. used to get correct graph file
= Schema Overview =
*ab_payload - number representing codec type
 
*ab_saddr - source IP address of the RTP stream
<syntaxhighlight lang="sql">
*ab_received - number of received RTP packets
-- List all CDR-related tables
*ab_lost - number of lost RTP packets detected by holes in sequence numbers according to rfc3550
SHOW TABLES LIKE 'cdr%';
*ab_ua_id - useragent reference to table cdr_ua  
</syntaxhighlight>
*ab_avgjitter_mult10 - average jitter calculated according to rfc3550
 
*ab_maxjitter - maximum jitter  
<kroki lang="plantuml">
*ab_sl1 - absl10 -  
@startuml
*b_d50-300
skinparam shadowing false
*a_mos_f1_mult10
skinparam defaultFontName Arial
*a_mos_f2_mult10
skinparam linetype ortho
*a_mos_adapt_mult10
 
*a_rtcp_loss
entity "cdr" as cdr {
*a_rtcp_maxfr
  * ID : bigint <<PK>>
*a_rtcp_avgfr_mult10
  --
*a_rtcp_maxjitter
  id_sensor, calldate, callend
*a_rtcp_avgjitter_mult10
  caller, called, duration
*payload
  sipcallerip, sipcalledip
*jitter_mult10
  a_lost, b_lost, a_mos_f1_mult10
*mos_min_mult10
  lastSIPresponse_id <<FK>>
*a_mos_min_mult10
  a_ua_id, b_ua_id <<FK>>
*packet_loss_perc_mult1000
}
*a_packet_loss_perc_mult1000
 
*delay_sum
entity "cdr_next" as cdr_next {
*a_delay_sum
  * cdr_ID <<PK,FK>>
*delay_avg_mult100
  --
*a_delay_avg_mult100
  fbasename, match_header
*delay_cnt
}
*a_delay_cnt
 
*rtcp_avgfr_mult10
entity "cdr_rtp" as cdr_rtp {
*rtcp_avgjitter_mult10
  cdr_ID <<FK>>
*lost
  --
  saddr, daddr, ssrc
  received, lost, payload
}
 
entity "cdr_proxy" as cdr_proxy {
  cdr_ID <<FK>>
  --
  dst (proxy IP)
}
 
entity "cdr_sdp" as cdr_sdp {
  cdr_id <<FK>>
  --
  ip, port, is_caller
}
 
entity "cdr_siphistory" as cdr_siphistory {
  cdr_ID <<FK>>
  --
  method, calldate, cseq
}
 
entity "cdr_ua" as cdr_ua {
  * ID <<PK>>
  --
  ua (User-Agent string)
}
 
entity "cdr_sip_response" as cdr_sip_response {
  * ID <<PK>>
  --
  lastSIPresponse
}
 
cdr ||--|| cdr_next : "1:1"
cdr ||--o{ cdr_rtp : "1:N"
cdr ||--o{ cdr_proxy : "1:N"
cdr ||--o{ cdr_sdp : "1:N"
cdr ||--o{ cdr_siphistory : "1:N"
cdr }o--|| cdr_ua : "N:1"
cdr }o--|| cdr_sip_response : "N:1"
@enduml
</kroki>
 
== Column Naming Conventions ==
 
{| class="wikitable"
|-
! Pattern !! Meaning !! Example
|-
| <code>a_</code> || Caller's stream (A-leg) || <code>a_lost</code>, <code>a_mos_f1_mult10</code>
|-
| <code>b_</code> || Callee's stream (B-leg) || <code>b_lost</code>, <code>b_mos_f1_mult10</code>
|-
| <code>_mult10</code> || Divide by 10 || <code>jitter_mult10 = 15</code> → jitter = 1.5
|-
| <code>_mult100</code> || Divide by 100 || Percentage values
|-
| <code>_mult1000</code> || Divide by 1000 || <code>packet_loss_perc_mult1000</code>
|}
 
= The <code>cdr</code> Table =
 
== Call Information & Timing ==
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>ID</code> || Primary key
|-
| <code>id_sensor</code> || Sensor ID (from <code>voipmonitor.conf</code>)
|-
| <code>calldate</code> / <code>callend</code> || Start/end timestamps
|-
| <code>duration</code> || Total length in seconds
|-
| <code>connect_duration</code> || Connected time (after 200 OK)
|-
| <code>progress_time</code> || Time to first provisional response ([[Glossary#Post_Dial_Delay_.28PDD.29|PDD]])
|-
| <code>bye</code> || <code>1</code> = normal termination with BYE
|-
| <code>whohanged</code> || Which party sent BYE (caller/callee)
|-
| <code>lastSIPresponse_id</code> || FK to <code>cdr_sip_response</code>
|}
 
== Participant Information ==
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>caller</code> / <code>called</code> || Numbers from From/To headers
|-
| <code>caller_domain</code> / <code>called_domain</code> || Domain parts
|-
| <code>sipcallerip</code> / <code>sipcalledip</code> || IPs stored as integers (use <code>INET_NTOA()</code>)
|-
| <code>a_ua_id</code> / <code>b_ua_id</code> || FK to <code>cdr_ua</code> (User-Agent)
|}
 
== Quality Metrics ==
 
=== Packet Loss ===
{| class="wikitable"
|-
! Column !! Description
|-
| <code>a_lost</code> / <code>b_lost</code> || Total RTP packets lost
|-
| <code>packet_loss_perc_mult1000</code> || Max loss percentage × 1000
|-
| <code>a_sl1</code> - <code>a_sl10</code> || Distribution of consecutive loss events
|}
 
=== Jitter & Delay ===
{| class="wikitable"
|-
! Column !! Description
|-
| <code>a_avgjitter_mult10</code> || Average jitter × 10
|-
| <code>a_maxjitter</code> || Maximum jitter observed
|-
| <code>a_d50</code> - <code>a_d300</code> || PDV distribution buckets (0-50ms, 50-70ms, etc.)
|}
 
=== MOS (Mean Opinion Score) ===
{| class="wikitable"
|-
! Column !! Description
|-
| <code>a_mos_f1_mult10</code> || [[Comprehensive_Guide_to_VoIP_Voice_Quality#MOS_Calculation_in_VoIPmonitor|MOS]] with 50ms fixed buffer × 10
|-
| <code>a_mos_f2_mult10</code> || MOS with 200ms fixed buffer × 10
|-
| <code>a_mos_adapt_mult10</code> || MOS with adaptive buffer × 10
|-
| <code>mos_min_mult10</code> || Lowest MOS between A/B streams × 10
|}
 
=== RTCP-Reported Metrics ===
These come from endpoint RTCP reports, not calculated by the sniffer. See [[Call_Detail_Record_-_CDR#Understanding_Sniffer_vs_RTCP_Packet_Loss|Sniffer vs RTCP Packet Loss]].
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>a_rtcp_loss</code> || Packets lost (reported by endpoint)
|-
| <code>a_rtcp_avgfr_mult10</code> || Average fraction loss × 10
|-
| <code>a_rtcp_avgjitter_mult10</code> || Average jitter × 10
|-
| <code>a_rtcp_avgrtd_mult10</code> || '''Roundtrip delay (latency)''' in ms × 10
|}
 
= Related Tables =
 
== <code>cdr_next</code> (1:1 with cdr) ==
 
Extension table linked via <code>cdr_ID</code>.
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>fbasename</code> || Derived from SIP Call-ID; used to link PCAP files. Special characters replaced with underscores (<code>convertchar</code> in config)
|-
| <code>match_header</code> || Content of custom header for linking call legs (<code>matchheader</code> in config)
|}
 
== <code>cdr_rtp</code> (Per-Stream RTP Stats) ==
 
One row per RTP stream. Multiple streams per call.
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>cdr_ID</code> || FK to <code>cdr</code>
|-
| <code>ssrc</code> || RTP Synchronization Source identifier
|-
| <code>saddr</code> / <code>daddr</code> || Source/destination IP (as integer)
|-
| <code>sport</code> / <code>dport</code> || Source/destination port
|-
| <code>payload</code> || Codec type. '''Video streams: payload ≥ 10000''' (offset applied)
|-
| <code>received</code> / <code>lost</code> || Packet counts
|}
 
{{Tip|To query video streams only: <code>WHERE payload >= 10000</code>}}
 
== <code>cdr_sdp</code> (SDP-Negotiated Ports) ==
 
Stores media endpoints declared in SDP. Requires <code>save_sdp_ipport = yes</code>.
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>ip</code> / <code>port</code> || Media IP and port from SDP
|-
| <code>is_caller</code> || 1 = caller side, 0 = callee side
|}
 
{{Note|<code>cdr_sdp</code> stores what endpoints '''agreed to use''' in SDP; <code>cdr_rtp</code> stores what was '''actually sent'''.}}
 
== <code>cdr_proxy</code> (Proxy Chain) ==
 
IP addresses of SIP proxies the call traversed.
 
== <code>cdr_siphistory</code> (SIP Method History) ==
 
Stores SIP requests/responses including REFER, BYE, CANCEL, OPTIONS, etc. Requires <code>save_sip_history = all</code>.
 
== <code>cdr_ua</code> / <code>cdr_sip_response</code> (Lookup Tables) ==
 
Lookup tables for User-Agent strings and SIP response texts.
 
= Common SQL Queries =
 
== Basic CDR Query with IP Conversion ==
 
<syntaxhighlight lang="sql">
SELECT
    ID, caller, called,
    INET_NTOA(sipcallerip) AS caller_ip,
    INET_NTOA(sipcalledip) AS called_ip,
    duration
FROM cdr
WHERE calldate >= '2024-01-01'
  AND sipcallerip = INET_ATON('192.168.1.50')
LIMIT 10;
</syntaxhighlight>
 
== Get PCAP Filename (fbasename) ==
 
<syntaxhighlight lang="sql">
SELECT c.ID, c.caller, c.called, cn.fbasename
FROM cdr c
JOIN cdr_next cn ON c.ID = cn.cdr_ID
WHERE c.ID = 12345;
</syntaxhighlight>
 
== Get User-Agent Strings ==
 
<syntaxhighlight lang="sql">
SELECT c.ID, ua_a.ua AS caller_ua, ua_b.ua AS callee_ua
FROM cdr c
LEFT JOIN cdr_ua ua_a ON c.a_ua_id = ua_a.ID
LEFT JOIN cdr_ua ua_b ON c.b_ua_id = ua_b.ID
WHERE c.calldate >= CURDATE() - INTERVAL 1 DAY
LIMIT 10;
</syntaxhighlight>
 
== Find Calls Through Specific Proxy ==
 
<syntaxhighlight lang="sql">
SELECT c.ID, c.calldate, c.caller, c.called,
      INET_NTOA(cp.dst) AS proxy_ip
FROM cdr c
JOIN cdr_proxy cp ON c.ID = cp.cdr_ID
WHERE cp.dst = INET_ATON('192.168.1.100')
LIMIT 100;
</syntaxhighlight>
 
== Query Video Stream Quality ==
 
<syntaxhighlight lang="sql">
SELECT cdr_ID, payload,
      INET_NTOA(saddr) AS src_ip,
      received, lost,
      (lost / (received + lost)) * 100 AS loss_pct
FROM cdr_rtp
WHERE payload >= 10000  -- Video streams only
  AND cdr_ID = 12345;
</syntaxhighlight>
 
== Find Calls with Specific SIP Method (REFER) ==
 
<syntaxhighlight lang="sql">
SELECT cdr_ID, callid, calldate
FROM cdr_siphistory
WHERE method = 'REFER'
  AND calldate > '2024-01-01'
GROUP BY cdr_ID;
</syntaxhighlight>
 
= Custom Headers =
 
== CDR-Level Custom Headers ==
 
CDR custom headers are stored in <code>cdr_next_X</code> tables. Use <code>cdr_custom_headers</code> to find the location:
 
<syntaxhighlight lang="sql">
-- Step 1: Find table/column for header
SELECT dynamic_table, dynamic_column
FROM cdr_custom_headers
WHERE header_field = 'X-Customer-ID';
-- Result: dynamic_table=2, dynamic_column=1 → cdr_next_2.custom_header_1
 
-- Step 2: Query the data
SELECT c.ID, c.calldate, cn.custom_header_1 AS customer_id
FROM cdr c
JOIN cdr_next_2 cn ON c.ID = cn.cdr_ID
WHERE cn.custom_header_1 = '12345';
</syntaxhighlight>
 
{{Note|The GUI's [[Reports|Reports]] module does not support grouping by custom headers. Use direct SQL queries.}}
=== <code>cdr_custom_headers</code> Table Structure ===
 
The <code>cdr_custom_headers</code> table stores the configuration for custom header extraction:
 
{| class="wikitable"
|-
! Column !! Description
|-
| <code>header_field</code> || SIP header name to capture
|-
| <code>dynamic_table</code> || Target table number (cdr_next_X)
|-
| <code>dynamic_column</code> || Target column number (custom_header_X)
|-
| <code>type</code> || Extraction type (first/last/nth)
|-
| <code>nth_occurrence</code> || '''(New in 35.x)''' Specific occurrence number to extract when type is "nth" (e.g., 2 for second occurrence)
|}
 
{{Note|The <code>nth_occurrence</code> column uses packet timestamps for accurate ordering, ensuring correct results even when packet time-based reordering is disabled in the sniffer.}}
== SIP Message Custom Headers ==
 
Per-message headers are stored in <code>sip_msg_next_X</code> tables. Use <code>sip_msg_custom_headers</code> to find the location.
 
= System Table =
 
The <code>system</code> table stores configuration including <code>manager_key</code> for sensor operations.
 
<syntaxhighlight lang="sql">
SELECT * FROM voipmonitor.`system` WHERE type='manager_key'\G
</syntaxhighlight>
 
{{Warning|1=If sniffer fails with "failed read rsa key", check that <code>mysqlloadconfig</code> is NOT set to <code>no</code> in <code>voipmonitor.conf</code>.}}
 
== Synchronizing manager_key Between Databases ==
 
When multiple GUI instances share sensors (e.g., after database migration), mismatched <code>manager_key</code> values cause PCAP download failures.
 
'''Symptoms:''' "Unable to download PCAP" errors, empty SIP history in one GUI.
 
'''Fix:'''
<syntaxhighlight lang="sql">
-- On active database: get the key
SELECT content FROM voipmonitor.`system` WHERE type='manager_key';
 
-- On old database: update the key
UPDATE voipmonitor.`system`
SET content = '{"key":"<key_from_active>","iv":"<iv_from_active>"}'
WHERE type = 'manager_key';
</syntaxhighlight>
 
Users must log out and log back in after the update.
 
= Troubleshooting =
 
== Missing <code>sport</code>/<code>dport</code> in cdr_rtp ==
 
If RTP streams from overlapping calls are incorrectly merged, add the missing columns:
 
<syntaxhighlight lang="sql">
ALTER TABLE cdr_rtp ADD `sport` smallint unsigned DEFAULT NULL AFTER `saddr`;
ALTER TABLE cdr_rtp ADD `dport` smallint unsigned DEFAULT NULL AFTER `daddr`;
</syntaxhighlight>
 
{{Warning|1=<code>ALTER TABLE</code> can lock large tables for hours. Run during maintenance windows.}}
 
To find other recommended schema changes:
<syntaxhighlight lang="bash">
journalctl -u voipmonitor | grep ALTER
</syntaxhighlight>
 
= See Also =
 
* [[Call_Detail_Record_-_CDR|CDR View (GUI)]]
* [[Database_troubleshooting|Database Troubleshooting]]
* [[Sniffer_configuration#Database_Configuration|Database Configuration]]
* [[WEB_API|Web API Documentation]]
 
 
= AI Summary for RAG =
 
'''Summary:''' VoIPmonitor database schema reference. Main table <code>cdr</code> stores one record per call with timing, participants, and quality metrics. Column naming: <code>a_</code>=caller, <code>b_</code>=callee, <code>_multNN</code>=divide by NN. Key tables: <code>cdr_next</code> (1:1, contains <code>fbasename</code> for PCAP linking), <code>cdr_rtp</code> (per-stream stats, video streams have payload≥10000), <code>cdr_proxy</code> (proxy chain IPs), <code>cdr_sdp</code> (SDP-negotiated ports), <code>cdr_siphistory</code> (SIP method history, requires <code>save_sip_history=all</code>). Custom headers stored in <code>cdr_next_X</code> tables; query <code>cdr_custom_headers</code> for mappings. System table stores <code>manager_key</code>; mismatch causes PCAP download failures.
 
'''Keywords:''' database, schema, cdr, mysql, cdr_next, fbasename, cdr_rtp, video payload, cdr_proxy, cdr_sdp, cdr_siphistory, INET_NTOA, INET_ATON, sipcallerip, custom headers, cdr_custom_headers, manager_key, packet loss, jitter, mos, rtcp, a_rtcp_avgrtd_mult10, latency
 
'''Key Questions:'''
* What do a_ and b_ prefixes mean in cdr table columns?
* How do I convert sipcallerip to readable IP address?
* How do I get the PCAP filename (fbasename) for a call?
* How do I query video streams in cdr_rtp (payload >= 10000)?
* How do I find calls that went through a specific proxy?
* How do I find calls with REFER or other SIP methods?
* How do I query CDR custom headers?
* Why does "failed read rsa key" error occur on startup?
* How do I sync manager_key between multiple databases?
* Why can't old GUI download PCAP files after database migration?

Latest revision as of 11:29, 19 January 2026


Database schema reference for VoIPmonitor. The cdr table stores one record per call; related tables store RTP statistics, custom headers, and metadata.

Schema Overview

-- List all CDR-related tables
SHOW TABLES LIKE 'cdr%';

Column Naming Conventions

Pattern Meaning Example
a_ Caller's stream (A-leg) a_lost, a_mos_f1_mult10
b_ Callee's stream (B-leg) b_lost, b_mos_f1_mult10
_mult10 Divide by 10 jitter_mult10 = 15 → jitter = 1.5
_mult100 Divide by 100 Percentage values
_mult1000 Divide by 1000 packet_loss_perc_mult1000

The cdr Table

Call Information & Timing

Column Description
ID Primary key
id_sensor Sensor ID (from voipmonitor.conf)
calldate / callend Start/end timestamps
duration Total length in seconds
connect_duration Connected time (after 200 OK)
progress_time Time to first provisional response (PDD)
bye 1 = normal termination with BYE
whohanged Which party sent BYE (caller/callee)
lastSIPresponse_id FK to cdr_sip_response

Participant Information

Column Description
caller / called Numbers from From/To headers
caller_domain / called_domain Domain parts
sipcallerip / sipcalledip IPs stored as integers (use INET_NTOA())
a_ua_id / b_ua_id FK to cdr_ua (User-Agent)

Quality Metrics

Packet Loss

Column Description
a_lost / b_lost Total RTP packets lost
packet_loss_perc_mult1000 Max loss percentage × 1000
a_sl1 - a_sl10 Distribution of consecutive loss events

Jitter & Delay

Column Description
a_avgjitter_mult10 Average jitter × 10
a_maxjitter Maximum jitter observed
a_d50 - a_d300 PDV distribution buckets (0-50ms, 50-70ms, etc.)

MOS (Mean Opinion Score)

Column Description
a_mos_f1_mult10 MOS with 50ms fixed buffer × 10
a_mos_f2_mult10 MOS with 200ms fixed buffer × 10
a_mos_adapt_mult10 MOS with adaptive buffer × 10
mos_min_mult10 Lowest MOS between A/B streams × 10

RTCP-Reported Metrics

These come from endpoint RTCP reports, not calculated by the sniffer. See Sniffer vs RTCP Packet Loss.

Column Description
a_rtcp_loss Packets lost (reported by endpoint)
a_rtcp_avgfr_mult10 Average fraction loss × 10
a_rtcp_avgjitter_mult10 Average jitter × 10
a_rtcp_avgrtd_mult10 Roundtrip delay (latency) in ms × 10

Related Tables

cdr_next (1:1 with cdr)

Extension table linked via cdr_ID.

Column Description
fbasename Derived from SIP Call-ID; used to link PCAP files. Special characters replaced with underscores (convertchar in config)
match_header Content of custom header for linking call legs (matchheader in config)

cdr_rtp (Per-Stream RTP Stats)

One row per RTP stream. Multiple streams per call.

Column Description
cdr_ID FK to cdr
ssrc RTP Synchronization Source identifier
saddr / daddr Source/destination IP (as integer)
sport / dport Source/destination port
payload Codec type. Video streams: payload ≥ 10000 (offset applied)
received / lost Packet counts

💡 Tip:

cdr_sdp (SDP-Negotiated Ports)

Stores media endpoints declared in SDP. Requires save_sdp_ipport = yes.

Column Description
ip / port Media IP and port from SDP
is_caller 1 = caller side, 0 = callee side

ℹ️ Note: cdr_sdp stores what endpoints agreed to use in SDP; cdr_rtp stores what was actually sent.

cdr_proxy (Proxy Chain)

IP addresses of SIP proxies the call traversed.

cdr_siphistory (SIP Method History)

Stores SIP requests/responses including REFER, BYE, CANCEL, OPTIONS, etc. Requires save_sip_history = all.

cdr_ua / cdr_sip_response (Lookup Tables)

Lookup tables for User-Agent strings and SIP response texts.

Common SQL Queries

Basic CDR Query with IP Conversion

SELECT
    ID, caller, called,
    INET_NTOA(sipcallerip) AS caller_ip,
    INET_NTOA(sipcalledip) AS called_ip,
    duration
FROM cdr
WHERE calldate >= '2024-01-01'
  AND sipcallerip = INET_ATON('192.168.1.50')
LIMIT 10;

Get PCAP Filename (fbasename)

SELECT c.ID, c.caller, c.called, cn.fbasename
FROM cdr c
JOIN cdr_next cn ON c.ID = cn.cdr_ID
WHERE c.ID = 12345;

Get User-Agent Strings

SELECT c.ID, ua_a.ua AS caller_ua, ua_b.ua AS callee_ua
FROM cdr c
LEFT JOIN cdr_ua ua_a ON c.a_ua_id = ua_a.ID
LEFT JOIN cdr_ua ua_b ON c.b_ua_id = ua_b.ID
WHERE c.calldate >= CURDATE() - INTERVAL 1 DAY
LIMIT 10;

Find Calls Through Specific Proxy

SELECT c.ID, c.calldate, c.caller, c.called,
       INET_NTOA(cp.dst) AS proxy_ip
FROM cdr c
JOIN cdr_proxy cp ON c.ID = cp.cdr_ID
WHERE cp.dst = INET_ATON('192.168.1.100')
LIMIT 100;

Query Video Stream Quality

SELECT cdr_ID, payload,
       INET_NTOA(saddr) AS src_ip,
       received, lost,
       (lost / (received + lost)) * 100 AS loss_pct
FROM cdr_rtp
WHERE payload >= 10000  -- Video streams only
  AND cdr_ID = 12345;

Find Calls with Specific SIP Method (REFER)

SELECT cdr_ID, callid, calldate
FROM cdr_siphistory
WHERE method = 'REFER'
  AND calldate > '2024-01-01'
GROUP BY cdr_ID;

Custom Headers

CDR-Level Custom Headers

CDR custom headers are stored in cdr_next_X tables. Use cdr_custom_headers to find the location:

-- Step 1: Find table/column for header
SELECT dynamic_table, dynamic_column
FROM cdr_custom_headers
WHERE header_field = 'X-Customer-ID';
-- Result: dynamic_table=2, dynamic_column=1 → cdr_next_2.custom_header_1

-- Step 2: Query the data
SELECT c.ID, c.calldate, cn.custom_header_1 AS customer_id
FROM cdr c
JOIN cdr_next_2 cn ON c.ID = cn.cdr_ID
WHERE cn.custom_header_1 = '12345';

ℹ️ Note: The GUI's Reports module does not support grouping by custom headers. Use direct SQL queries.

cdr_custom_headers Table Structure

The cdr_custom_headers table stores the configuration for custom header extraction:

Column Description
header_field SIP header name to capture
dynamic_table Target table number (cdr_next_X)
dynamic_column Target column number (custom_header_X)
type Extraction type (first/last/nth)
nth_occurrence (New in 35.x) Specific occurrence number to extract when type is "nth" (e.g., 2 for second occurrence)

ℹ️ Note: The nth_occurrence column uses packet timestamps for accurate ordering, ensuring correct results even when packet time-based reordering is disabled in the sniffer.

SIP Message Custom Headers

Per-message headers are stored in sip_msg_next_X tables. Use sip_msg_custom_headers to find the location.

System Table

The system table stores configuration including manager_key for sensor operations.

SELECT * FROM voipmonitor.`system` WHERE type='manager_key'\G

⚠️ Warning: If sniffer fails with "failed read rsa key", check that mysqlloadconfig is NOT set to no in voipmonitor.conf.

Synchronizing manager_key Between Databases

When multiple GUI instances share sensors (e.g., after database migration), mismatched manager_key values cause PCAP download failures.

Symptoms: "Unable to download PCAP" errors, empty SIP history in one GUI.

Fix:

-- On active database: get the key
SELECT content FROM voipmonitor.`system` WHERE type='manager_key';

-- On old database: update the key
UPDATE voipmonitor.`system`
SET content = '{"key":"<key_from_active>","iv":"<iv_from_active>"}'
WHERE type = 'manager_key';

Users must log out and log back in after the update.

Troubleshooting

Missing sport/dport in cdr_rtp

If RTP streams from overlapping calls are incorrectly merged, add the missing columns:

ALTER TABLE cdr_rtp ADD `sport` smallint unsigned DEFAULT NULL AFTER `saddr`;
ALTER TABLE cdr_rtp ADD `dport` smallint unsigned DEFAULT NULL AFTER `daddr`;

⚠️ Warning: ALTER TABLE can lock large tables for hours. Run during maintenance windows.

To find other recommended schema changes:

journalctl -u voipmonitor | grep ALTER

See Also


AI Summary for RAG

Summary: VoIPmonitor database schema reference. Main table cdr stores one record per call with timing, participants, and quality metrics. Column naming: a_=caller, b_=callee, _multNN=divide by NN. Key tables: cdr_next (1:1, contains fbasename for PCAP linking), cdr_rtp (per-stream stats, video streams have payload≥10000), cdr_proxy (proxy chain IPs), cdr_sdp (SDP-negotiated ports), cdr_siphistory (SIP method history, requires save_sip_history=all). Custom headers stored in cdr_next_X tables; query cdr_custom_headers for mappings. System table stores manager_key; mismatch causes PCAP download failures.

Keywords: database, schema, cdr, mysql, cdr_next, fbasename, cdr_rtp, video payload, cdr_proxy, cdr_sdp, cdr_siphistory, INET_NTOA, INET_ATON, sipcallerip, custom headers, cdr_custom_headers, manager_key, packet loss, jitter, mos, rtcp, a_rtcp_avgrtd_mult10, latency

Key Questions:

  • What do a_ and b_ prefixes mean in cdr table columns?
  • How do I convert sipcallerip to readable IP address?
  • How do I get the PCAP filename (fbasename) for a call?
  • How do I query video streams in cdr_rtp (payload >= 10000)?
  • How do I find calls that went through a specific proxy?
  • How do I find calls with REFER or other SIP methods?
  • How do I query CDR custom headers?
  • Why does "failed read rsa key" error occur on startup?
  • How do I sync manager_key between multiple databases?
  • Why can't old GUI download PCAP files after database migration?