Skip to main content

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

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:

  1. OmniTAS sets tas_call_reason=credit_exhausted channel variable
  2. Schedules transfer to credit_exhausted extension in ims_as dialplan context
  3. 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 ResponseActionLogs
{:ok, 0} (Zero seconds)Immediate credit exhaustion hangupCredit exhausted (zero seconds allocated) - triggering immediate hangup
{:error, 4012} (CREDIT_LIMIT_REACHED)Immediate credit exhaustion hangupCredit exhausted (4012 CREDIT_LIMIT_REACHED) - triggering immediate hangup
{:error, 4010} (END_USER_SERVICE_DENIED)Immediate credit exhaustion hangupService denied (4010 END_USER_SERVICE_DENIED) - triggering immediate hangup
{:error, reason} (Other errors)Stop periodic CCR job, scheduled timer firesPeriodic CCR failed with error <reason> - Stopping job
{:ok, N} where N > 0Reschedule timer to +N secondsPeriodic 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:

  1. 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
  2. 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:

  1. Grouped AVPs add a level to the variable name hierarchy but have no value themselves
  2. Simple AVPs are mapped to variables with their full dotted path
  3. Vendor-Specific AVPs are processed identically to standard AVPs
  4. 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

ParameterTypeRequiredDefaultDescription
enabledBooleanNofalseEnable online charging integration. When false, all calls bypass OCS authorization.
periodic_ccr_time_secondsIntegerNo60Interval 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_secondsIntegerNo2Safety 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_authBooleanNofalseEnable 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_announcementStringNonilAudio 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_regexList[String]No[]List of regex patterns for destination numbers that bypass OCS. Useful for emergency numbers (e.g., "^911$", "^000$").

Diameter Connection Parameters

ParameterTypeRequiredDefaultDescription
origin_hostStringYes-OmniTAS Diameter Identity (FQDN). Must be unique across your Diameter network. Example: "tas01.epc.mnc123.mcc456.3gppnetwork.org".
origin_realmStringYes-OmniTAS Diameter Realm. Used for routing decisions. Example: "epc.mnc123.mcc456.3gppnetwork.org".
destination_realmStringYes-OCS Diameter Realm. Requests are routed to peers in this realm.
destination_hostStringNonilSpecific 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:

  1. Destination number is checked against skipped_regex patterns
  2. If matched, call bypasses OCS (useful for emergency services)
  3. If not matched, CCR-Initial sent to OCS at destination_realm
  4. CCA response is parsed for granted units and AVPs
  5. AVPs are mapped to FreeSWITCH variables (see AVP Mapping)
  6. Call proceeds with allocated_time and AVP data available
  7. CCR-Update sent every periodic_ccr_time_seconds during call
  8. If schedule_hangup_auth enabled, automatic hangup when credit expires
  9. CCR-Terminate sent on call completion

Use cases:

  • Basic OCS: Enable with defaults for standard credit control
  • High-value calls: Reduce periodic_ccr_time_seconds to 30s for frequent re-auth
  • Prepaid service: Enable schedule_hangup_auth and set credit_exhaustion_announcement
  • Emergency compliance: Add emergency numbers to skipped_regex to 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:

AVPCodeTypeDescription
Session-Id263UTF8StringUnique session identifier: <origin_host>;<timestamp>;<random>
Auth-Application-Id258Unsigned32Value 4 for Diameter Credit Control Application per RFC 4006
Service-Context-Id461UTF8String"000.000.12.32260@3gpp.org" for IMS charging per TS 32.299
CC-Request-Type416EnumeratedValue 1 (INITIAL_REQUEST)
CC-Request-Number415Unsigned32Sequence number, starts at 1
Subscription-Id443GroupedSubscriber MSISDN or IMSI
Requested-Service-Unit437GroupedRequested credit (time or units)
Service-Information873GroupedIMS-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:

AVPCodeTypeDescription
Result-Code268Unsigned322001 for success. See Result Codes for error values.
Granted-Service-Unit431GroupedAllocated credit (time in seconds)
Service-Information873GroupedAdditional 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 update
  • Used-Service-Unit: Reported usage since last request
  • Requested-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 duration
  • Termination-Cause: Reason for session end

Result Codes

CodeNameDescriptionOmniTAS Action
2001DIAMETER_SUCCESSRequest approvedParse AVPs, setup call
4010DIAMETER_END_USER_SERVICE_DENIEDService denied for subscriberReject call with CALL_REJECTED
4012DIAMETER_CREDIT_LIMIT_REACHEDInsufficient creditReject call with OUTGOING_CALL_BARRED
5003DIAMETER_AUTHORIZATION_REJECTEDOCS policy deniedReject call with CALL_REJECTED
5xxxPermanent failuresOCS configuration or system errorReject 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: ccr
  • status - 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 - ro
  • command - ccr
  • result_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, error
  • skipped - true if bypassed via regex, false otherwise

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 - ro
  • command - ccr
  • status - 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:

  1. OCS not returning Service-Information AVPs in CCA
  2. AVP parsing failed due to unexpected structure
  3. Variables not exported to FreeSWITCH channel

Resolution:

  1. 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.

  2. 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.

  3. Verify FreeSWITCH Variable Export

    In FreeSWITCH console or ESL:

    freeswitch> uuid_dump <call-uuid>

    Look for variables with the variable_ prefix and CCA. 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: 2001

    Note: 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:

  1. Capture CCA packet structure from logs
  2. Check if AVPs are in expected Diameter format
  3. 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_realm or destination_host
  • OCS not responding to requests

Resolution:

  1. Verify Network Connectivity

    Test TCP connection to OCS:

    telnet ocs.example.com 3868

    Should connect successfully. If connection refused or timeout, check firewall rules.

  2. Check Diameter Configuration

    Verify destination_realm matches OCS configuration:

    config :tas, :diameter,
    destination_realm: "epc.mnc380.mcc313.3gppnetwork.org" # Must match OCS realm
  3. Review OCS Logs

    Check OCS for incoming CCR messages. If OCS receives requests but doesn't respond:

    • Verify OmniTAS origin_host is recognized by OCS
    • Check OCS peer configuration allows connections from OmniTAS
    • Verify Service-Context-Id and Application-Id match OCS expectations

Credit Exhaustion Not Hanging Up Calls

Symptoms:

  • Calls continue beyond granted credit time
  • No automatic hangup when allocated_time expires
  • schedule_hangup_auth enabled but not working

Possible causes:

  • FreeSWITCH scheduled hangup not configured
  • schedule_hangup_auth is false
  • Call state not tracked properly

Resolution:

  1. Verify Configuration

    Ensure schedule_hangup_auth is enabled:

    config :tas, :online_charging,
    schedule_hangup_auth: true
  2. 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.

  3. Monitor Call State

    Check that call UUID is tracked in call state:

    [debug] Setting Scheduled Hangup for call in 600 seconds

    If 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_regex patterns are not bypassed
  • Delays on emergency calls

Possible causes:

  • Regex pattern syntax error
  • Destination number format mismatch
  • Regex not properly escaped

Resolution:

  1. Verify Regex Patterns

    Test regex compilation:

    Regex.compile("^911$")  # Should return {:ok, ~r/^911$/}

    Common mistakes:

    • Missing anchors: Use ^911$ not 911
    • Escaping: Use \* for literal asterisk, not \*
  2. 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.

  3. 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

SpecificationTitleRelevant Sections
TS 32.299Diameter charging applications§6.3 (Ro interface), §7.2 (AVP definitions)
TS 32.240Charging architecture and principles§5 (Online charging)
TS 29.229Cx and Dx interfacesService-Information AVP usage in IMS

IETF RFCs

RFCTitleRelevant Sections
RFC 6733Diameter Base Protocol§3 (Protocol overview), §7 (Error handling)
RFC 4006Diameter Credit-Control Application§8 (Credit-Control messages)

AVP Codes Reference

Common AVPs used in OCS integration:

AVP NameCodeVendor-IDTypeDescription
Session-Id2630UTF8StringUnique session identifier
Auth-Application-Id2580Unsigned32Diameter application ID (4 for CC)
CC-Request-Type4160Enumerated1=Initial, 2=Update, 3=Terminate
CC-Request-Number4150Unsigned32Sequence number
Result-Code2680Unsigned32Request result (2001=success)
Granted-Service-Unit4310GroupedAllocated credit
CC-Time4200Unsigned32Time quota in seconds
Service-Information87310415Grouped3GPP service-specific data
IMS-Information87610415GroupedIMS charging information
Carrier-Select-Routing-Information202310415UTF8StringCarrier routing code
Alternate-Charged-Party-Address128010415UTF8StringBilling party identifier

Vendor-ID 10415 = 3GPP

FreeSWITCH Channel Variables

All extracted AVP data is available as FreeSWITCH channel variables:

Variable NameSourceExample ValueDescription
${allocated_time}Granted-Service-Unit / CC-Time600Allocated time in seconds
${CCA.Session-Id}Session-Id AVPomni-as01.epc...;1769299669873;325e2f2eDiameter session identifier
${CCA.Result-Code}Result-Code AVP2001CCA result (2001 = success)
${CCA.Auth-Application-Id}Auth-Application-Id AVP4Diameter application (4 = CC)
${CCA.CC-Request-Type}CC-Request-Type AVP1Request type (1=Initial)
${CCA.CC-Request-Number}CC-Request-Number AVP1Sequence number
${CCA.CC-Time}CC-Time AVP (if present)600Granted time quota
${CCA.Origin-Host}Origin-Host AVPocs01.epc.mnc380.mcc313.3gppnetwork.orgOCS host identifier
${CCA.Origin-Realm}Origin-Realm AVPepc.mnc380.mcc313.3gppnetwork.orgOCS realm
${CCA.Service-Information.Carrier-Select-Routing-Information}Service-Information → Carrier-Select-Routing-Information1408Carrier routing code from OCS
${CCA.Service-Information.Alternate-Charged-Party-Address}Service-Information → Alternate-Charged-Party-AddressNickTestAlternate 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