Online Charging System (OCS) Integration
Comprehensive guide to OmniTAS integration with Online Charging Systems via Diameter Ro interface, including real-time credit control, AVP extraction, and FreeSWITCH variable mapping.
Table of Contents
- Architecture Overview
- Credit Control Flow
- AVP Parsing and Variable Mapping
- Configuration
- FreeSWITCH Integration
- Diameter Messages
- Metrics
- Troubleshooting
- Reference
Architecture Overview
OmniTAS implements the Diameter Ro interface per 3GPP TS 32.299 for real-time online charging. The system authorizes calls by requesting credit from an OCS before call setup, monitors credit during the call, and reports final usage on termination.
Key Components
Credit-Control-Request (CCR):
- CCR-Initial (Type 1): Sent before call setup to request initial credit authorization
- CCR-Update (Type 2): Sent during active calls for re-authorization or interim updates
- CCR-Terminate (Type 3): Sent on call termination with final usage reporting
Credit-Control-Answer (CCA):
- Contains granted service units (time quota in seconds)
- Includes vendor-specific AVPs with additional charging data
- Provides routing information, charged party details, and service identifiers
Credit Control Flow
Call Authorization Sequence
Credit Exhaustion Handling
OmniTAS supports multiple mechanisms for handling credit exhaustion, with automatic integration between scheduled hangups and credit exhaustion announcements.
Scheduled Hangup with Dynamic Rescheduling
When schedule_hangup_auth is enabled, OmniTAS schedules a FreeSWITCH timer that automatically terminates calls when granted credit expires. This timer is dynamically rescheduled every time new credit is granted via CCR-Update responses.
How it works:
Buffer Logic:
OmniTAS sends CCR-Update messages before the granted credit expires to ensure continuous service. The buffer time is configurable via ccr_update_buffer_seconds (default: 2 seconds).
Example timeline:
- T+0s: Call answered, OCS grants 10s, timer scheduled for T+10s
- T+8s: CCR-U sent (10s - 2s buffer)
- T+8.1s: OCS grants 10s, timer rescheduled to T+18.1s (10s from now)
- T+16.1s: CCR-U sent
- T+16.2s: OCS grants 10s, timer rescheduled to T+26.2s
- Call continues as long as OCS keeps granting credit
Logs to watch:
[OCS HANGUP RESCHEDULE] Found UUID <uuid> for call <id> - rescheduling timer to 10s from now
[SCHED TRANSFER] Scheduling transfer to credit_exhausted dialplan for <uuid> in 10s
[OCS HANGUP RESCHEDULE] Successfully rescheduled timer for call <id> (UUID: <uuid>)
Integration: schedule_hangup_auth + credit_exhaustion_announcement
When both features are enabled, OmniTAS automatically uses scheduled transfers instead of direct hangups, allowing the caller to hear an announcement before call termination.
Without announcement configured:
config :tas, :online_charging,
schedule_hangup_auth: true,
credit_exhaustion_announcement: nil
→ Uses sched_hangup - direct hangup when credit expires
With announcement configured:
config :tas, :online_charging,
schedule_hangup_auth: true,
credit_exhaustion_announcement: "${base_dir}/sounds/en/us/callie/misc/8000/credit_exhausted.wav"
→ Uses sched_transfer - transfers to credit_exhausted dialplan which plays announcement then hangs up
How the transfer works:
- OmniTAS sets
tas_call_reason=credit_exhaustedchannel variable - Schedules transfer to
credit_exhaustedextension inims_asdialplan context - When timer fires:
- FreeSWITCH transfers A-leg to credit_exhausted dialplan
- Bridge breaks automatically, B-leg receives BYE
- Dialplan plays announcement to A-leg
- Call terminates after announcement
Benefits:
- Caller hears professional announcement instead of abrupt disconnect
- B-leg (called party) doesn't hear announcement
- CCR-T still sent with actual usage
- Announcement path: Must be relative to FreeSWITCH base directory (use
${base_dir}variable)
Immediate Credit Exhaustion During CCR-Update
If the OCS denies credit or returns zero seconds during a CCR-Update, OmniTAS immediately triggers credit exhaustion handling, overriding any scheduled timer.
OCS Response Scenarios:
Handled Error Codes:
| OCS Response | Action | Logs |
|---|---|---|
{:ok, 0} (Zero seconds) | Immediate credit exhaustion hangup | Credit exhausted (zero seconds allocated) - triggering immediate hangup |
{:error, 4012} (CREDIT_LIMIT_REACHED) | Immediate credit exhaustion hangup | Credit exhausted (4012 CREDIT_LIMIT_REACHED) - triggering immediate hangup |
{:error, 4010} (END_USER_SERVICE_DENIED) | Immediate credit exhaustion hangup | Service denied (4010 END_USER_SERVICE_DENIED) - triggering immediate hangup |
{:error, reason} (Other errors) | Stop periodic CCR job, scheduled timer fires | Periodic CCR failed with error <reason> - Stopping job |
{:ok, N} where N > 0 | Reschedule timer to +N seconds | Periodic CCA allocated Ns, will send next CCR-U in (N-buffer)s |
Priority: Immediate credit exhaustion handling wins over scheduled timer. If OCS denies credit at T+8s but timer was scheduled for T+10s, the immediate hangup at T+8s occurs and the scheduled timer becomes irrelevant.
Example timeline with mid-call credit denial:
T+0s: Call answered
T+0.1s: OCS grants 10s → Timer scheduled for T+10.1s
T+8s: CCR-U sent (buffer = 2s)
T+8.1s: OCS returns 0 seconds → Immediate transfer to credit_exhausted dialplan
T+8.2s: Announcement plays to caller
T+10s: Call terminated (scheduled timer irrelevant)
Logs for immediate credit exhaustion:
[warning] Credit exhausted (zero seconds allocated) - triggering immediate hangup
[warning] Hanging up call <id> (UUID: <uuid>) due to credit exhaustion
[info] Credit exhaustion announcement config: "${base_dir}/sounds/..."
[info] Playing announcement before hangup: ...
[info] Setting tas_call_reason=credit_exhausted for <uuid>
[info] Transferring to credit exhausted dialplan: uuid_transfer <uuid> credit_exhausted XML ims_as
Summary: Credit Exhaustion Mechanisms
OmniTAS provides two complementary mechanisms:
-
Scheduled Timer (
schedule_hangup_auth):- Automatic hangup/transfer when granted credit expires
- Dynamically rescheduled on each CCR-U response
- Uses buffer logic to send CCR-U before expiration
- Integrates with announcement feature
-
Immediate Exhaustion Handling:
- Triggered when OCS denies credit during CCR-U
- Overrides scheduled timer
- Supports announcement playback
- Handles specific Diameter error codes
Both mechanisms respect the credit_exhaustion_announcement configuration and will play the configured audio before terminating calls when configured.
AVP Parsing and Variable Mapping
Overview
OmniTAS automatically extracts Attribute-Value Pairs (AVPs) from Credit-Control-Answer messages and makes them available to FreeSWITCH as channel variables. This enables dialplan logic to use OCS-provided data for routing decisions, billing purposes, or call treatment.
Supported AVP Types:
- Simple values (UTF8String, Unsigned32, Integer32)
- Grouped AVPs with nested structures
- Vendor-specific AVPs (e.g., 3GPP Service-Information)
Variable Naming Convention:
AVPs are flattened into dot-notation channel variables with the prefix CCA:
CCA.<AVP-Name>.<Nested-AVP-Name>.<Value-AVP-Name> = "value"
Common AVP Mappings
Service-Information AVP (3GPP)
The Service-Information grouped AVP (AVP Code 873, Vendor-ID 10415) contains IMS-specific charging details:
Example OCS Response:
Service-Information
├── IMS-Information
│ ├── Carrier-Select-Routing-Information: "1408"
│ └── Node-Functionality: 6
└── Alternate-Charged-Party-Address: "NickTest"
Resulting FreeSWITCH Variables:
CCA.Service-Information.Carrier-Select-Routing-Information = "1408"
CCA.Service-Information.Alternate-Charged-Party-Address = "NickTest"
Accessing in Dialplan: Variables use dot notation and hyphens as shown above:
<action application="log" data="INFO Carrier: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>
Viewing with uuid_dump:
In FreeSWITCH console or ESL, variables appear with the variable_ prefix:
variable_CCA.Service-Information.Carrier-Select-Routing-Information: 1408
variable_CCA.Service-Information.Alternate-Charged-Party-Address: NickTest
Note: FreeSWITCH preserves dots and hyphens in variable names. The variables work in all dialplan contexts and applications.
Granted-Service-Unit AVP
Time quotas are extracted and made available:
OCS Response:
Granted-Service-Unit
└── CC-Time: 600
Variable:
allocated_time = 600
AVP Processing Logic
Processing Rules:
- Grouped AVPs add a level to the variable name hierarchy but have no value themselves
- Simple AVPs are mapped to variables with their full dotted path
- Vendor-Specific AVPs are processed identically to standard AVPs
- Unknown AVPs are safely skipped without errors
Example: Multi-Level Nesting
OCS CCA Structure:
Service-Information (Grouped)
├── IMS-Information (Grouped)
│ ├── Node-Functionality: 6
│ ├── Role-Of-Node: 1
│ ├── Calling-Party-Address: "tel:+313380000000670"
│ └── Time-Stamps (Grouped)
│ ├── SIP-Request-Timestamp: "2026-01-24T22:40:18Z"
│ └── SIP-Response-Timestamp: "2026-01-24T22:40:18Z"
└── IN-Information (Grouped)
└── Real-Called-Number: "24724741234"
FreeSWITCH Variables Created:
CCA.Service-Information.IMS-Information.Node-Functionality = "6"
CCA.Service-Information.IMS-Information.Role-Of-Node = "1"
CCA.Service-Information.IMS-Information.Calling-Party-Address = "tel:+313380000000670"
CCA.Service-Information.IMS-Information.Time-Stamps.SIP-Request-Timestamp = "2026-01-24T22:40:18Z"
CCA.Service-Information.IMS-Information.Time-Stamps.SIP-Response-Timestamp = "2026-01-24T22:40:18Z"
CCA.Service-Information.IN-Information.Real-Called-Number = "24724741234"
Configuration
Online Charging Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | false | Enable online charging integration. When false, all calls bypass OCS authorization. |
periodic_ccr_time_seconds | Integer | No | 60 | Interval in seconds between CCR-Update messages during active calls. Not used when schedule_hangup_auth is enabled (dynamic timing based on granted credit). Recommended range: 30-300 seconds for legacy mode. |
ccr_update_buffer_seconds | Integer | No | 2 | Safety buffer in seconds before credit expires when sending CCR-Update. OmniTAS sends CCR-U at (allocated_time - buffer) to ensure credit is extended before expiration. Recommended: 2-5 seconds. |
schedule_hangup_auth | Boolean | No | false | Enable automatic call hangup/transfer when granted credit expires. When true, OmniTAS schedules FreeSWITCH timer based on allocated_time from each CCA and dynamically reschedules on every CCR-U response. Works with credit_exhaustion_announcement. |
credit_exhaustion_announcement | String | No | nil | Audio file path for credit exhaustion announcement. When configured with schedule_hangup_auth, uses scheduled transfer to play announcement before hangup. When configured alone (without schedule_hangup_auth), plays announcement on immediate credit exhaustion only. Path must use FreeSWITCH variable: "${base_dir}/sounds/...". Set to nil for direct hangup without announcement. |
skipped_regex | List[String] | No | [] | List of regex patterns for destination numbers that bypass OCS. Useful for emergency numbers (e.g., "^911$", "^000$"). |
Diameter Connection Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
origin_host | String | Yes | - | OmniTAS Diameter Identity (FQDN). Must be unique across your Diameter network. Example: "tas01.epc.mnc123.mcc456.3gppnetwork.org". |
origin_realm | String | Yes | - | OmniTAS Diameter Realm. Used for routing decisions. Example: "epc.mnc123.mcc456.3gppnetwork.org". |
destination_realm | String | Yes | - | OCS Diameter Realm. Requests are routed to peers in this realm. |
destination_host | String | No | nil | Specific OCS Diameter Identity. When nil, routing based on destination_realm only. Use when direct routing to specific OCS instance required. |
Configuration Example
config :tas, :online_charging,
# Enable online charging
enabled: true,
# Send CCR-Update every 60 seconds
periodic_ccr_time_seconds: 60,
# Schedule hangup based on granted credit
schedule_hangup_auth: true,
# Play announcement before credit exhaustion hangup
credit_exhaustion_announcement: "ivr/ivr-account_balance_low.wav",
# Skip OCS for emergency calls and voicemail
skipped_regex: [
"^911$", # Emergency (US)
"^000$", # Emergency (AU)
"^\*86$" # Voicemail access
]
config :tas, :diameter,
# Service identity
origin_host: "tas01.epc.mnc380.mcc313.3gppnetwork.org",
origin_realm: "epc.mnc380.mcc313.3gppnetwork.org",
# OCS routing
destination_realm: "epc.mnc380.mcc313.3gppnetwork.org",
destination_host: nil # Realm-based routing
How it works:
When a call is received:
- Destination number is checked against
skipped_regexpatterns - If matched, call bypasses OCS (useful for emergency services)
- If not matched, CCR-Initial sent to OCS at
destination_realm - CCA response is parsed for granted units and AVPs
- AVPs are mapped to FreeSWITCH variables (see AVP Mapping)
- Call proceeds with
allocated_timeand AVP data available - CCR-Update sent every
periodic_ccr_time_secondsduring call - If
schedule_hangup_authenabled, automatic hangup when credit expires - CCR-Terminate sent on call completion
Use cases:
- Basic OCS: Enable with defaults for standard credit control
- High-value calls: Reduce
periodic_ccr_time_secondsto 30s for frequent re-auth - Prepaid service: Enable
schedule_hangup_authand setcredit_exhaustion_announcement - Emergency compliance: Add emergency numbers to
skipped_regexto ensure always connected
FreeSWITCH Integration
Accessing AVP Variables in Dialplan
AVP data extracted from CCA messages is available as channel variables in FreeSWITCH dialplan:
<extension name="Route_with_OCS_Data">
<condition field="destination_number" expression="^(.+)$">
<!-- Access carrier routing info from OCS -->
<action application="log"
data="INFO Carrier Code: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>
<!-- Access charged party from OCS -->
<action application="log"
data="INFO Charged Party: ${CCA.Service-Information.Alternate-Charged-Party-Address}"/>
<!-- Access granted time -->
<action application="log"
data="INFO Allocated Time: ${allocated_time} seconds"/>
<!-- Route based on carrier code -->
<action application="set"
data="carrier_code=${CCA.Service-Information.Carrier-Select-Routing-Information}"/>
<action application="bridge"
data="sofia/external/$1@carrier-${carrier_code}.sip.example.com"/>
</condition>
</extension>
Variable Availability
Timing:
- Variables are set before FreeSWITCH call setup
- Available throughout entire call duration
- Persist across call transfers and updates
Scope:
- Channel-scoped (specific to individual call leg)
- Not inherited by bridged/transferred legs
- Safe to use in all dialplan applications
Example Use Cases
1. Carrier Selection Based on OCS Data
Use OCS-provided carrier code to route calls:
<extension name="Carrier_Selection">
<condition field="${CCA.Service-Information.Carrier-Select-Routing-Information}" expression="^(.+)$">
<action application="bridge"
data="sofia/external/${destination_number}@carrier-$1.example.com"/>
</condition>
<!-- Fallback if no carrier specified -->
<condition field="${CCA.Service-Information.Carrier-Select-Routing-Information}" expression="^$">
<action application="bridge"
data="sofia/external/${destination_number}@default-carrier.example.com"/>
</condition>
</extension>
How it works: OCS returns carrier code "1408" in Service-Information AVP. FreeSWITCH routes call to carrier-1408.example.com gateway based on this data.
2. Alternate Billing Party
Route billing to a different party based on OCS response:
<extension name="Alternate_Billing">
<condition field="${CCA.Service-Information.Alternate-Charged-Party-Address}" expression="^(.+)$">
<!-- Log billing party for CDRs -->
<action application="set"
data="billed_party=$1"/>
<action application="export"
data="billed_party=$1"/>
<!-- Include in SIP headers -->
<action application="set"
data="sip_h_X-Billed-Party=$1"/>
<action application="bridge"
data="sofia/external/${destination_number}@trunk.example.com"/>
</condition>
</extension>
How it works: OCS specifies alternate charged party (e.g., corporate account). OmniTAS extracts "NickTest" from AVP and makes it available to dialplan for CDR recording and SIP header insertion.
3. Time-Limited Calls with Warnings
Provide warnings before credit expires:
<extension name="Credit_Warnings">
<condition field="destination_number" expression="^(.+)$">
<!-- Schedule warning 30 seconds before hangup -->
<action application="set"
data="warning_time=${expr(${allocated_time} - 30)}"/>
<action application="sched_hangup"
data="+${allocated_time} ALLOTTED_TIMEOUT"/>
<action application="sched_broadcast"
data="+${warning_time} playback::ivr/ivr-account_balance_low.wav"/>
<action application="bridge"
data="sofia/external/$1@trunk.example.com"/>
</condition>
</extension>
How it works: Uses allocated_time from OCS to schedule automatic hangup and plays warning announcement 30 seconds before disconnection.
Diameter Messages
CCR-Initial (Request Type 1)
Sent before call setup to request authorization and initial credit allocation.
Key AVPs Sent:
| AVP | Code | Type | Description |
|---|---|---|---|
| Session-Id | 263 | UTF8String | Unique session identifier: <origin_host>;<timestamp>;<random> |
| Auth-Application-Id | 258 | Unsigned32 | Value 4 for Diameter Credit Control Application per RFC 4006 |
| Service-Context-Id | 461 | UTF8String | "000.000.12.32260@3gpp.org" for IMS charging per TS 32.299 |
| CC-Request-Type | 416 | Enumerated | Value 1 (INITIAL_REQUEST) |
| CC-Request-Number | 415 | Unsigned32 | Sequence number, starts at 1 |
| Subscription-Id | 443 | Grouped | Subscriber MSISDN or IMSI |
| Requested-Service-Unit | 437 | Grouped | Requested credit (time or units) |
| Service-Information | 873 | Grouped | IMS-specific call details (calling/called party, timestamps) |
Example CCR-I:
Session-Id: "tas01.example.org;1769294418268;8a078232"
Auth-Application-Id: 4
CC-Request-Type: 1 (INITIAL_REQUEST)
CC-Request-Number: 1
Subscription-Id:
- Subscription-ID-Type: 0 (END_USER_E164)
Subscription-ID-Data: "313380000000670"
Requested-Service-Unit:
- CC-Time: 0 (Request maximum available)
Service-Information:
- IMS-Information:
- Calling-Party-Address: "tel:+313380000000670"
- Called-Party-Address: "tel:+24724741234"
- Node-Functionality: 6 (AS)
CCA (Credit-Control-Answer)
Response from OCS with authorization decision and granted credit.
Key AVPs Received:
| AVP | Code | Type | Description |
|---|---|---|---|
| Result-Code | 268 | Unsigned32 | 2001 for success. See Result Codes for error values. |
| Granted-Service-Unit | 431 | Grouped | Allocated credit (time in seconds) |
| Service-Information | 873 | Grouped | Additional charging data (carrier info, charged party, etc.) |
Example CCA with AVPs:
Session-Id: "tas01.example.org;1769294418268;8a078232"
Result-Code: 2001 (DIAMETER_SUCCESS)
CC-Request-Type: 1
CC-Request-Number: 1
Granted-Service-Unit:
- CC-Time: 600 (10 minutes granted)
Service-Information:
- IMS-Information:
- Carrier-Select-Routing-Information: "1408"
- Alternate-Charged-Party-Address: "NickTest"
Resulting Variables:
allocated_time = 600
CCA.Service-Information.Carrier-Select-Routing-Information = "1408"
CCA.Service-Information.Alternate-Charged-Party-Address = "NickTest"
CCR-Update (Request Type 2)
Sent during active calls for periodic re-authorization or interim usage reporting.
When Sent:
- Every
periodic_ccr_time_seconds(default: 60s) - On call answer (transition from setup to active)
- When explicitly triggered (e.g., service change)
Key Differences from CCR-I:
CC-Request-Type:2(UPDATE_REQUEST)CC-Request-Number: Increments with each updateUsed-Service-Unit: Reported usage since last requestRequested-Service-Unit: Additional credit requested
CCR-Terminate (Request Type 3)
Sent on call termination with final usage reporting.
Key AVPs:
CC-Request-Type:3(TERMINATION_REQUEST)Used-Service-Unit: Total call durationTermination-Cause: Reason for session end
Result Codes
| Code | Name | Description | OmniTAS Action |
|---|---|---|---|
| 2001 | DIAMETER_SUCCESS | Request approved | Parse AVPs, setup call |
| 4010 | DIAMETER_END_USER_SERVICE_DENIED | Service denied for subscriber | Reject call with CALL_REJECTED |
| 4012 | DIAMETER_CREDIT_LIMIT_REACHED | Insufficient credit | Reject call with OUTGOING_CALL_BARRED |
| 5003 | DIAMETER_AUTHORIZATION_REJECTED | OCS policy denied | Reject call with CALL_REJECTED |
| 5xxx | Permanent failures | OCS configuration or system error | Reject call, log error |
Reference: RFC 6733 §7.1 and 3GPP TS 32.299
Metrics
Diameter Request Metrics
Metric: diameter_requests_total
Type: Counter
Description: Total Diameter requests sent by application and request type
Labels:
application- Diameter application:ro(online charging)command- Request type:ccrstatus- Result:success,error,timeout
Example queries:
# CCR success rate
sum(rate(diameter_requests_total{application="ro",command="ccr",status="success"}[5m]))
/ sum(rate(diameter_requests_total{application="ro",command="ccr"}[5m]))
# CCR timeout rate
rate(diameter_requests_total{application="ro",command="ccr",status="timeout"}[5m])
Diameter Response Metrics
Metric: diameter_responses_total
Type: Counter
Description: Diameter responses received by result code
Labels:
application-rocommand-ccrresult_code- Diameter result code (2001, 4012, etc.)
Example queries:
# Responses by result code
sum by (result_code) (rate(diameter_responses_total{application="ro"}[5m]))
# Credit limit rejections (4012)
rate(diameter_responses_total{application="ro",result_code="4012"}[5m])
OCS Authorization Metrics
Metric: ocs_authorizations_total
Type: Counter
Description: OCS authorization attempts and outcomes
Labels:
result-success,nocredit,timeout,errorskipped-trueif bypassed via regex,falseotherwise
Example queries:
# Authorization success rate (excluding skipped)
sum(rate(ocs_authorizations_total{result="success",skipped="false"}[5m]))
/ sum(rate(ocs_authorizations_total{skipped="false"}[5m]))
# No-credit rejections
rate(ocs_authorizations_total{result="nocredit"}[5m])
Diameter Duration Metrics
Metric: diameter_request_duration_seconds
Type: Histogram
Description: Diameter request round-trip time
Labels:
application-rocommand-ccrstatus-success,error,timeout
Example queries:
# 95th percentile CCR latency
histogram_quantile(0.95,
sum(rate(diameter_request_duration_seconds_bucket{application="ro"}[5m])) by (le)
)
# Average latency by status
avg(rate(diameter_request_duration_seconds_sum{application="ro"}[5m]))
by (status)
/ avg(rate(diameter_request_duration_seconds_count{application="ro"}[5m]))
by (status)
Troubleshooting
AVP Variables Not Available in FreeSWITCH
Symptoms:
- FreeSWITCH dialplan cannot access
${CCA.Service-Information.*}variables - Variables show as empty or undefined
Possible causes:
- OCS not returning Service-Information AVPs in CCA
- AVP parsing failed due to unexpected structure
- Variables not exported to FreeSWITCH channel
Resolution:
-
Verify OCS Response Contains AVPs
Check OmniTAS logs for CCA message:
[debug] Credit Control Answer: {:diameter_packet, ...}
[debug] Parsed AVP variables: %{
"CCA.Service-Information.Carrier-Select-Routing-Information" => "1408",
"CCA.Service-Information.Alternate-Charged-Party-Address" => "NickTest"
}If "Parsed AVP variables" is empty
%{}, OCS is not returning the expected AVPs. -
Check for AVP Parsing Errors
Look for warnings in logs:
[warning] got back another type of reply: {...}This indicates AVP structure doesn't match expected format. Check Diameter packet structure.
-
Verify FreeSWITCH Variable Export
In FreeSWITCH console or ESL:
freeswitch> uuid_dump <call-uuid>Look for variables with the
variable_prefix andCCA.in the name:variable_CCA.Service-Information.Carrier-Select-Routing-Information: 1408
variable_CCA.Service-Information.Alternate-Charged-Party-Address: NickTest
variable_CCA.Auth-Application-Id: 4
variable_CCA.Result-Code: 2001Note: FreeSWITCH preserves dots and hyphens in variable names. They work correctly in dialplan:
<action application="log" data="Carrier: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>
Call Rejected with "unhandled" Error
Symptoms:
- Logs show:
[warning] Could not authorize call: :unhandled - Valid CCA responses (Result-Code 2001) are rejected
- Calls fail despite OCS approving them
Possible causes:
- CCA message structure doesn't match expected pattern
- Vendor-specific AVPs in unexpected positions
- AVP position index mismatch
Resolution:
This was a known issue fixed in recent releases. Ensure you're running current version.
Previous behavior: Pattern matching required:
- Granted-Service-Unit AVP at position 7 exactly
- Empty vendor-specific AVP list
[]
Current behavior: Pattern matching accepts:
- Granted-Service-Unit AVP at any position
- Non-empty vendor-specific AVP lists
If issue persists:
- Capture CCA packet structure from logs
- Check if AVPs are in expected Diameter format
- Verify Result-Code is 2001
OCS Timeout on All Requests
Symptoms:
- All CCR requests timeout
- Logs show:
[debug] Got back response for authorize: {:error, :timeout} - No CCA received within 5 seconds
Possible causes:
- Network connectivity to OCS/DRA
- Firewall blocking Diameter port (3868)
- Incorrect
destination_realmordestination_host - OCS not responding to requests
Resolution:
-
Verify Network Connectivity
Test TCP connection to OCS:
telnet ocs.example.com 3868Should connect successfully. If connection refused or timeout, check firewall rules.
-
Check Diameter Configuration
Verify
destination_realmmatches OCS configuration:config :tas, :diameter,
destination_realm: "epc.mnc380.mcc313.3gppnetwork.org" # Must match OCS realm -
Review OCS Logs
Check OCS for incoming CCR messages. If OCS receives requests but doesn't respond:
- Verify OmniTAS
origin_hostis recognized by OCS - Check OCS peer configuration allows connections from OmniTAS
- Verify Service-Context-Id and Application-Id match OCS expectations
- Verify OmniTAS
Credit Exhaustion Not Hanging Up Calls
Symptoms:
- Calls continue beyond granted credit time
- No automatic hangup when
allocated_timeexpires schedule_hangup_authenabled but not working
Possible causes:
- FreeSWITCH scheduled hangup not configured
schedule_hangup_authisfalse- Call state not tracked properly
Resolution:
-
Verify Configuration
Ensure
schedule_hangup_authis enabled:config :tas, :online_charging,
schedule_hangup_auth: true -
Check FreeSWITCH ESL Connection
Verify OmniTAS can send commands to FreeSWITCH:
[debug] Schedule Hangup Response: {:ok, "+OK"}If error or no response, check FreeSWITCH Event Socket configuration.
-
Monitor Call State
Check that call UUID is tracked in call state:
[debug] Setting Scheduled Hangup for call in 600 secondsIf UUID not found, call state tracking may have issues.
Skipped Regex Not Bypassing OCS
Symptoms:
- Emergency calls (911, 000) still go through OCS authorization
- Numbers matching
skipped_regexpatterns are not bypassed - Delays on emergency calls
Possible causes:
- Regex pattern syntax error
- Destination number format mismatch
- Regex not properly escaped
Resolution:
-
Verify Regex Patterns
Test regex compilation:
Regex.compile("^911$") # Should return {:ok, ~r/^911$/}Common mistakes:
- Missing anchors: Use
^911$not911 - Escaping: Use
\*for literal asterisk, not\*
- Missing anchors: Use
-
Check Number Format
Verify destination number format matches pattern:
[debug] Checking if dialled number "911" matches skipped regex...If number is formatted as "+1911" but pattern is "^911$", it won't match.
-
Example Patterns
config :tas, :online_charging,
skipped_regex: [
"^911$", # US Emergency
"^000$", # AU Emergency
"^112$", # International Emergency
"^\*86$", # Voicemail (escaped asterisk)
"^1?800\d{7}$" # Toll-free numbers
]
Reference
3GPP Specifications
| Specification | Title | Relevant Sections |
|---|---|---|
| TS 32.299 | Diameter charging applications | §6.3 (Ro interface), §7.2 (AVP definitions) |
| TS 32.240 | Charging architecture and principles | §5 (Online charging) |
| TS 29.229 | Cx and Dx interfaces | Service-Information AVP usage in IMS |
IETF RFCs
| RFC | Title | Relevant Sections |
|---|---|---|
| RFC 6733 | Diameter Base Protocol | §3 (Protocol overview), §7 (Error handling) |
| RFC 4006 | Diameter Credit-Control Application | §8 (Credit-Control messages) |
AVP Codes Reference
Common AVPs used in OCS integration:
| AVP Name | Code | Vendor-ID | Type | Description |
|---|---|---|---|---|
| Session-Id | 263 | 0 | UTF8String | Unique session identifier |
| Auth-Application-Id | 258 | 0 | Unsigned32 | Diameter application ID (4 for CC) |
| CC-Request-Type | 416 | 0 | Enumerated | 1=Initial, 2=Update, 3=Terminate |
| CC-Request-Number | 415 | 0 | Unsigned32 | Sequence number |
| Result-Code | 268 | 0 | Unsigned32 | Request result (2001=success) |
| Granted-Service-Unit | 431 | 0 | Grouped | Allocated credit |
| CC-Time | 420 | 0 | Unsigned32 | Time quota in seconds |
| Service-Information | 873 | 10415 | Grouped | 3GPP service-specific data |
| IMS-Information | 876 | 10415 | Grouped | IMS charging information |
| Carrier-Select-Routing-Information | 2023 | 10415 | UTF8String | Carrier routing code |
| Alternate-Charged-Party-Address | 1280 | 10415 | UTF8String | Billing party identifier |
Vendor-ID 10415 = 3GPP
FreeSWITCH Channel Variables
All extracted AVP data is available as FreeSWITCH channel variables:
| Variable Name | Source | Example Value | Description |
|---|---|---|---|
${allocated_time} | Granted-Service-Unit / CC-Time | 600 | Allocated time in seconds |
${CCA.Session-Id} | Session-Id AVP | omni-as01.epc...;1769299669873;325e2f2e | Diameter session identifier |
${CCA.Result-Code} | Result-Code AVP | 2001 | CCA result (2001 = success) |
${CCA.Auth-Application-Id} | Auth-Application-Id AVP | 4 | Diameter application (4 = CC) |
${CCA.CC-Request-Type} | CC-Request-Type AVP | 1 | Request type (1=Initial) |
${CCA.CC-Request-Number} | CC-Request-Number AVP | 1 | Sequence number |
${CCA.CC-Time} | CC-Time AVP (if present) | 600 | Granted time quota |
${CCA.Origin-Host} | Origin-Host AVP | ocs01.epc.mnc380.mcc313.3gppnetwork.org | OCS host identifier |
${CCA.Origin-Realm} | Origin-Realm AVP | epc.mnc380.mcc313.3gppnetwork.org | OCS realm |
${CCA.Service-Information.Carrier-Select-Routing-Information} | Service-Information → Carrier-Select-Routing-Information | 1408 | Carrier routing code from OCS |
${CCA.Service-Information.Alternate-Charged-Party-Address} | Service-Information → Alternate-Charged-Party-Address | NickTest | Alternate billing party |
Variable Format:
- All CCA AVPs use the prefix
CCA. - Nested AVPs use dot notation:
CCA.Parent.Child - Dots and hyphens are preserved in variable names
- In uuid_dump, variables appear with
variable_prefix
Example uuid_dump output:
variable_allocated_time: 600
variable_CCA.Service-Information.Carrier-Select-Routing-Information: 1408
variable_CCA.Service-Information.Alternate-Charged-Party-Address: NickTest
variable_CCA.Result-Code: 2001