跳到主要内容

在线计费系统 (OCS) 集成

全面指南,介绍如何通过Diameter Ro接口将OmniTAS与在线计费系统集成,包括实时信用控制、AVP提取和FreeSWITCH变量映射。

目录

架构概述

OmniTAS根据3GPP TS 32.299实现Diameter Ro接口,以进行实时在线计费。系统在呼叫设置之前通过请求OCS的信用来授权呼叫,在呼叫期间监控信用,并在终止时报告最终使用情况。

关键组件

信用控制请求 (CCR):

  • CCR-Initial (类型1): 在呼叫设置之前发送以请求初始信用授权
  • CCR-Update (类型2): 在活动呼叫期间发送以进行重新授权或临时更新
  • CCR-Terminate (类型3): 在呼叫终止时发送,报告最终使用情况

信用控制应答 (CCA):

  • 包含授予的服务单位(以秒为单位的时间配额)
  • 包括带有附加计费数据的供应商特定AVPs
  • 提供路由信息、收费方详细信息和服务标识符

控制呼叫是否收费的因素

呼叫仅在以下三个条件均满足时才会生成Diameter CCRs。它们在OmniTAS为呼叫构建移动发起(MO)拨号计划时进行评估:

  1. 在线计费已启用online_charging.enabledtrue。当为 false 时,OmniTAS会在本地授权每个呼叫,并且从不联系OCS。
  2. 呼叫是移动发起 (MO) — 只有MO腿会收费。移动终止 (MT) 呼叫永远不会收费:MT路径不执行OCS授权,因此不会为其创建信用控制会话。
  3. 目的地不在豁免名单中 — 拨打的号码不匹配 skipped_regex 中的任何模式(例如紧急号码、服务代码)。

如果这三条均成立,OmniTAS将发送 CCR-Initial 并打开一个以SIP Call-ID为键的信用控制会话。从那时起,CCR-UpdateCCR-Terminate 由应答/挂断事件驱动(见 应答和挂断通知),并且仅为具有开放会话的呼叫腿生成。MT腿、桥接B腿和豁免呼叫因此永远不会产生CCRs。

注意: 到达OCS并不保证呼叫连接。成功授权时授予 单位会阻止呼叫 — 见 信用耗尽与呼叫禁止。禁止发生在发送CCR之��;它与上述决定是否发送CCR的条件不同。

信用控制流程

呼叫授权序列

信用耗尽与呼叫禁止

呼叫设置时的禁止。 如果OCS返回成功的CCA(结果代码 2001)但授予的单位为 — 或显式拒绝,如 4012 (DIAMETER_CREDIT_LIMIT_REACHED) 或 4010 (DIAMETER_END_USER_SERVICE_DENIED) — OmniTAS将该呼叫视为没有信用,并以挂断原因 OUTGOING_CALL_BARRED 禁止该呼叫。订阅者永远不会连接。这是未配置或信用耗尽的订阅者的预期结果。

通话中耗尽。 如果在设置时授予了信用,但后来耗尽(定期CCR-Update返回零单位或信用限制错误),OmniTAS将终止正在进行的呼叫。配置了 credit_exhaustion_announcement 时,它会在挂断之前将呼叫者转移到公告;否则,它会直接挂断。

CCR-Terminate在耗尽时触发,在任何清理之前。 当余额耗尽时,OmniTAS会 立即 发送CCR-Terminate(收费应答→耗尽),然后 播放信用耗尽公告或挂断。这是故意的:订阅者花费在“您已耗尽余额”提示上花费的时间是 不收费 的,因此CDR必须在信用耗尽时结束 — 而不是在用户从录音中挂断时结束。会话在那时终止,因此最终FreeSWITCH对呼叫的挂断找不到会话并且不发送任何内容(幂等 — 没有第二个CCR-T)。无论清理是转移到公告还是直接挂断,这都适用,因此不依赖于FreeSWITCH挂断原因。

OmniTAS支持多种处理信用耗尽的机制,自动集成计划挂断和信用耗尽公告。

动态重新调度的计划挂断

schedule_hangup_auth 启用时,OmniTAS会调度一个FreeSWITCH定时器,在授予的信用到期时自动终止呼叫。每次通过CCR-Update响应授予新信用时,该定时器会 动态重新调度

工作原理:

缓冲逻辑:

OmniTAS在授予的信用到期之前发送CCR-Update消息,以确保持续服务。缓冲时间可以通过 ccr_update_buffer_seconds 配置(默认:2秒)。

示例时间线:

  • T+0秒: 呼叫接听,OCS授予10秒,定时器调度为T+10秒
  • T+8秒: 发送CCR-U(10秒 - 2秒缓冲)
  • T+8.1秒: OCS授予10秒,定时器重新调度到T+18.1秒(从现在起10秒)
  • T+16.1秒: 发送CCR-U
  • T+16.2秒: OCS授予10秒,定时器重新调度到T+26.2秒
  • 呼叫继续,只要OCS继续授予信用

监控日志:

[OCS HANGUP RESCHEDULE] 找到UUID <uuid> 的呼叫 <id> - 将定时器重新调度到从现在起10秒
[SCHED TRANSFER] 在10秒内调度转移到credit_exhausted拨号计划 <uuid>
[OCS HANGUP RESCHEDULE] 成功重新调度呼叫 <id> 的定时器 (UUID: <uuid>)

集成:schedule_hangup_auth + credit_exhaustion_announcement

两者 功能都启用时,OmniTAS会自动使用计划 转移 而不是直接挂断,允许呼叫者在呼叫终止之前听到公告。

未配置公告时:

config :tas, :online_charging,
schedule_hangup_auth: true,
credit_exhaustion_announcement: nil

→ 使用 sched_hangup - 当信用到期时直接挂断

配置了公告时:

config :tas, :online_charging,
schedule_hangup_auth: true,
credit_exhaustion_announcement: "${base_dir}/sounds/en/us/callie/misc/8000/credit_exhausted.wav"

→ 使用 sched_transfer - 转移到 credit_exhausted 拨号计划,该计划播放公告然后挂断

转移的工作原理:

  1. OmniTAS设置 tas_call_reason=credit_exhausted 通道变量
  2. 调度转移到 ims_as 拨号计划上下文中的 credit_exhausted 分机
  3. 当定时器触发时:
    • FreeSWITCH将A腿转移到credit_exhausted拨号计划
    • 桥接自动断开,B腿接收BYE
    • 拨号计划向A腿播放公告
    • 呼叫在公告后终止

好处:

  • 呼叫者听到专业公告,而不是突然断开
  • B腿(被叫方)不会听到公告
  • CCR-T仍然发送实际使用情况
  • 公告路径:必须相对于FreeSWITCH基础目录(使用 ${base_dir} 变量)

CCR-Update期间的即时信用耗尽

如果OCS在CCR-Update期间 拒绝信用 或返回 零秒,OmniTAS会立即触发信用耗尽处理,覆盖任何计划的定时器。

OCS响应场景:

处理的错误代码:

OCS响应操作日志
{:ok, 0} (零秒)立即信用耗尽挂断信用耗尽(分配零秒) - 触发立即挂断
{:error, 4012} (CREDIT_LIMIT_REACHED)立即信用耗尽挂断信用耗尽(4012 CREDIT_LIMIT_REACHED) - 触发立即挂断
{:error, 4010} (END_USER_SERVICE_DENIED)立即信用耗尽挂断服务拒绝(4010 END_USER_SERVICE_DENIED) - 触发立即挂断
{:error, reason} (其他错误)停止定期CCR作业,计划定时器触发定期CCR因错误<reason>失败 - 停止作业
{:ok, N} 其中N > 0重新调度定时器到+N秒定期CCA分配Ns,将在(N-缓冲)秒内发送下一个CCR-U

优先级: 立即信用耗尽处理 优先 于计划定时器。如果OCS在T+8秒拒绝信用,但定时器计划在T+10秒,则在T+8秒发生立即挂断,计划定时器变得无关紧要。

示例时间线:

T+0秒:   呼叫接听
T+0.1秒: OCS授予10秒 → 定时器计划为T+10.1秒
T+8秒: 发送CCR-U(缓冲=2秒)
T+8.1秒: OCS返回0秒 → 立即转移到credit_exhausted拨号计划
T+8.2秒: 向呼叫者播放公告
T+10秒: 呼叫终止(计划定时器无关紧要)

立即信用耗尽的日志:

[warning] 信用耗尽(分配零秒) - 触发立即挂断
[warning] 由于信用耗尽,挂断呼叫 <id> (UUID: <uuid>)
[info] 信用耗尽公告配置: "${base_dir}/sounds/..."
[info] 在挂断之前播放公告: ...
[info] 为 <uuid> 设置 tas_call_reason=credit_exhausted
[info] 转移到信用耗尽拨号计划: uuid_transfer <uuid> credit_exhausted XML ims_as

总结:信用耗尽机制

OmniTAS提供两种互补机制:

  1. 计划定时器 (schedule_hangup_auth):

    • 当授予的信用到期时自动挂断/转移
    • 在每个CCR-U响应时动态重新调度
    • 使用缓冲逻辑在到期之前发送CCR-U
    • 与公告功能集成
  2. 即时耗尽处理:

    • 当OCS在CCR-U期间拒绝信用时触发
    • 覆盖计划定时器
    • 支持公告播放
    • 处理特定的Diameter错误代码

这两种机制都遵循 credit_exhaustion_announcement 配置,并将在配置时在终止呼叫之前播放配置的音频。

AVP解析和变量映射

概述

OmniTAS自动从信用控制应答消息中提取属性值对(AVPs),并将其作为通道变量提供给FreeSWITCH。这使得拨号计划逻辑可以使用OCS提供的数据进行路由决策、计费目的或呼叫处理。

支持的AVP类型:

  • 简单值(UTF8String、Unsigned32、Integer32)
  • 带有嵌套结构的分组AVPs
  • 供应商特定AVPs(例如,3GPP服务信息)

变量命名约定: AVPs被扁平化为带有前缀 CCA 的点表示法通道变量:

CCA.<AVP-Name>.<Nested-AVP-Name>.<Value-AVP-Name> = "value"

常见AVP映射

服务信息AVP (3GPP)

服务信息分组AVP(AVP代码873,供应商ID 10415)包含IMS特定的计费详细信息:

示例OCS响应:

服务信息
├── IMS信息
│ ├── 运营商选择路由信息: "1408"
│ └── 节点功能: 6
└── 备用收费方地址: "NickTest"

结果FreeSWITCH变量:

CCA.Service-Information.Carrier-Select-Routing-Information = "1408"
CCA.Service-Information.Alternate-Charged-Party-Address = "NickTest"

在拨号计划中访问: 变量使用点表示法和连字符,如上所示:

<action application="log" data="INFO 运营商: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>

使用uuid_dump查看: 在FreeSWITCH控制台或ESL中,变量以 variable_ 前缀出现:

variable_CCA.Service-Information.Carrier-Select-Routing-Information: 1408
variable_CCA.Service-Information.Alternate-Charged-Party-Address: NickTest

注意: FreeSWITCH保留变量名称中的点和连字符。这些变量在所有拨号计划上下文和应用程序中均有效。

授予服务单位AVP

时间配额被提取并提供:

OCS响应:

授予服务单位
└── CC时间: 600

变量:

allocated_time = 600

AVP处理逻辑

处理规则:

  1. 分组AVPs为变量名称层次结构添加一层,但本身没有值
  2. 简单AVPs被映射到具有其完整点路径的变量
  3. 供应商特定AVPs与标准AVPs的处理方式相同
  4. 未知AVPs安全跳过而不产生错误

示例:多级嵌套

OCS CCA结构:

服务信息 (分组)
├── IMS信息 (分组)
│ ├── 节点功能: 6
│ ├── 节点角色: 1
│ ├── 呼叫方地址: "tel:+313380000000670"
│ └── 时间戳 (分组)
│ ├── SIP请求时间戳: "2026-01-24T22:40:18Z"
│ └── SIP响应时间戳: "2026-01-24T22:40:18Z"
└── IN信息 (分组)
└── 实际拨打的号码: "24724741234"

创建的FreeSWITCH变量:

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"

配置

在线计费参数

参数类型必需默认描述
enabled布尔false在线计费的主开关。当为 false 时,所有 呼叫绕过OCS授权(不发送CCRs)。见 控制呼叫是否收费的因素
requested_units_seconds整数0在CCR-Initial/Update的 Requested-Service-Unit 中请求的通话时间(秒)。0(默认)发送一个 RSU(AVP存在但不携带 CC-Time),让OCS选择授予 — 这与忽略请求单位并计算其自身授予的诺基亚TAS匹配。设置一个 正值 来为符合标准的OCS保留特定的 CC-Time,参见 RFC 4006
report_and_reserve布尔false3GPP SCUR使用报告,匹配诺基亚TAS。当为 true 时,每个CCR-Update和CCR-Terminate都会携带一个 Used-Service-Unit,报告��上次报告以来消耗的 CC-Time(增量),加上 3GPP-Reporting-Reason:在应答更新时为 RATING_CONDITION_CHANGE (6),在定期更新时为 QUOTA_EXHAUSTED (3),在终止时为 FINAL (2)。OCS扣除每个增量,并在终止时结算最后授予的未使用部分。当为 false 时,仅CCR-T报告使用情况(从应答开始的累积,没有原因)。通话的增量 CC-Time 总和等于应答时长。
service_identifier整数1在每个CCR中发送的稳定、预配置的 Service-Identifier(AVP 439)。OCS根据 {Service-Context-Id + Service-Identifier} 进行计费,因此这必须是一个固定值,而不是随机值。
periodic_ccr_time_seconds整数10当OCS未返回可用授予时,CCR-Update消息之间的回退间隔(秒)。在正常操作中,下一个CCR-U的时间是根据授予的信用动态定时的(allocated_time - ccr_update_buffer_seconds)。
ccr_update_buffer_seconds整数2在授予的信用到期之前的安全缓冲(秒)。OmniTAS在 (allocated_time - buffer) 时发送下一个CCR-U,以便在信用耗尽之前延长信用。建议:2-5秒。
schedule_hangup_auth布尔false启用在授予的信用到期时自动挂断/转移呼叫。当为 true 时,OmniTAS根据每���CCA的 allocated_time 调度一个FreeSWITCH定时器,并在每个CCR-U响应时重新调度。与 credit_exhaustion_announcement 一起工作。
credit_exhaustion_announcement字符串nil信用耗尽公告的音频文件路径。当与 schedule_hangup_auth 一起配置时,使用计划 转移 在挂断之前播放公告。当单独配置时,仅在立即信用耗尽时播放公告。路径必须使用FreeSWITCH变量形式:"${base_dir}/sounds/..."。设置为 nil 以直接挂断而不播放公告。
skipped_regex列表[字符串][]目标号码 匹配的正则表达式模式列表。匹配的目的地完全绕过OCS(没有会话,没有CCRs)。对于紧急号码和服务代码(例如,"^911$""^000$")非常有用。

Diameter连接参数

参数类型必需默认描述
origin_host字符串-OmniTAS Diameter身份(FQDN)。在您的Diameter网络中必须唯一。例如:"tas01.epc.mnc123.mcc456.3gppnetwork.org"
origin_realm字符串-OmniTAS Diameter领域。用于路由决策。例如:"epc.mnc123.mcc456.3gppnetwork.org"
destination_realm字符串-OCS Diameter领域。请求路由到该领域中的对等��。
destination_host字符串nil特定OCS Diameter身份。当为 nil 时,仅根据 destination_realm 进行路由。当需要直接路由到特定OCS实例时使用。

配置示例

config :tas, :online_charging,
# 主开关
enabled: true,

# 0 = 空RSU,让OCS决定授予(与诺基亚TAS / CGRateS匹配)。
# 仅在符合标准的OCS上设置正值,该OCS根据请求的单位进行计费。
requested_units_seconds: 0,

# 稳定的预配置服务标识符
service_identifier: 1,

# 回退重新认证间隔(正常定时是动态的)
periodic_ccr_time_seconds: 10,

# 在授予的信用到期前2秒重新认证
ccr_update_buffer_seconds: 2,

# 根据授予的信用调度挂断
schedule_hangup_auth: true,

# 在信用耗尽挂断之前播放公告
credit_exhaustion_announcement: "ivr/ivr-account_balance_low.wav",

# 跳过紧急呼叫和语音信箱
skipped_regex: [
"^911$", # 紧急(美国)
"^000$", # 紧急(澳大利亚)
"^\*86$" # 语音信箱访问
]

config :tas, :diameter,
# 服务身份
origin_host: "tas01.epc.mnc001.mcc001.3gppnetwork.org",
origin_realm: "epc.mnc001.mcc001.3gppnetwork.org",

# OCS路由
destination_realm: "epc.mnc001.mcc001.3gppnetwork.org",
destination_host: nil # 基于领域的路由

工作原理:

当接收到呼叫时:

  1. 目标号码与 skipped_regex 模式进行检查
  2. 如果匹配,呼叫绕过OCS(对于紧急服务非常有用)
  3. 如果不匹配,CCR-Initial发送到OCS的 destination_realm
  4. 解析CCA响应以获取授予的单位和AVPs
  5. 将AVPs映射到FreeSWITCH变量(见 AVP映射
  6. 呼叫继续,allocated_time 和AVP数据可用
  7. 在呼叫期间每 periodic_ccr_time_seconds 发送CCR-Update
  8. 如果启用了 schedule_hangup_auth,则在信用到期时自动挂断
  9. 在呼叫完成时发送CCR-Terminate

用例:

  • 基本OCS: 启用默认设置以进行标准信用控制
  • 高价值呼叫: 将 periodic_ccr_time_seconds 减少到30秒以进行频繁的重新认证
  • 预付费服务: 启用 schedule_hangup_auth 并设置 credit_exhaustion_announcement
  • 紧急合规性: 将紧急号码添加到 skipped_regex 中以确保始终连接

FreeSWITCH集成

在拨号计划中访问AVP变量

从CCA消息中提取的AVP数据作为通道变量在FreeSWITCH拨号计划中可用:

<extension name="Route_with_OCS_Data">
<condition field="destination_number" expression="^(.+)$">

<!-- 访问来自OCS的运营商路由信息 -->
<action application="log"
data="INFO 运营商代码: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>

<!-- 访问来自OCS的收费方 -->
<action application="log"
data="INFO 收费方: ${CCA.Service-Information.Alternate-Charged-Party-Address}"/>

<!-- 访问授予的时间 -->
<action application="log"
data="INFO 分配时间: ${allocated_time} 秒"/>

<!-- 根据运营商代码路由 -->
<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>

变量可用性

时机:

  • 变量在FreeSWITCH呼叫设置 之前 设置
  • 在整个呼叫持续期间可用
  • 在呼叫转移和更新之间保持

范围:

  • 通道范围(特定于单个呼叫腿)
  • 不被桥接/转移的腿继承
  • 在所有拨号计划应用程序中安全使用

示例用例

1. 基于OCS数据的运营商选择

使用OCS提供的运营商代码路由呼叫:

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

<!-- 如果未指定运营商则回退 -->
<condition field="${CCA.Service-Information.Carrier-Select-Routing-Information}" expression="^$">
<action application="bridge"
data="sofia/external/${destination_number}@default-carrier.example.com"/>
</condition>
</extension>

工作原理: OCS在服务信息AVP中返回运营商代码“1408”。FreeSWITCH根据此数据将呼叫路由到 carrier-1408.example.com 网关。

2. 备用计费方

根据OCS响应将计费路由到不同的方:

<extension name="Alternate_Billing">
<condition field="${CCA.Service-Information.Alternate-Charged-Party-Address}" expression="^(.+)$">

<!-- 记录计费方以供CDR使用 -->
<action application="set"
data="billed_party=$1"/>
<action application="export"
data="billed_party=$1"/>

<!-- 包含在SIP头中 -->
<action application="set"
data="sip_h_X-Billed-Party=$1"/>

<action application="bridge"
data="sofia/external/${destination_number}@trunk.example.com"/>
</condition>
</extension>

��作原理: OCS指定备用收费方(例如公司账户)。OmniTAS从AVP中提取“NickTest”,并使其可用于拨号计划以进行CDR记录和SIP头插入。

3. 有时间限制的呼叫和警告

在信用到期之前提供警告:

<extension name="Credit_Warnings">
<condition field="destination_number" expression="^(.+)$">

<!-- 在挂断前30秒调度警告 -->
<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>

工作原理: 使用来自OCS的 allocated_time 调度自动挂断,并在断开连接前30秒播放警告公告。

应答和挂断通知

在CCR-Initial授权呼叫后,OmniTAS仍然需要知道 呼叫何时被应答(以开始计量并发送CCR-Update)和 何时结束(以发送CCR-Terminate)。这些事件通过FreeSWITCH的事件套接层(ESL)获取。

工作原理:

  • OmniTAS持有与本地FreeSWITCH的持久入站ESL连接,并订阅 CHANNEL_ANSWERCHANNEL_HANGUP_COMPLETE
  • CHANNEL_ANSWER 上,它读取SIP呼叫ID和实际应答时间,并发送开始计量的CCR-Update。
  • CHANNEL_HANGUP_COMPLETE 上,它读取SIP呼叫ID和FreeSWITCH挂断原因,并发送最终使用时间的CCR-Terminate。
  • 事件通过SIP呼叫ID与信用控制会话匹配。没有会话的呼叫腿(MT腿、桥接B腿、豁免呼叫)的事件被忽略 — 这就是防止未收费呼叫产生CCRs的原因。
  • 重复应答是幂等的。 桥接呼叫的两个腿都会触发 CHANNEL_ANSWER,并且可以解析到相同的呼叫ID;只有第一个发送CCR-Update并启动定期发送,其他的被忽略(否则会产生重复的CCR-U和双重定期发送��。
  • 转移结束收费会话。 BLIND_TRANSFER/ATTENDED_TRANSFER 的挂断被视为正常挂断 → CCR-Terminate(收费方已离开呼叫,或呼叫已转移到信用耗尽公告)。一旦会话终止,任何后续的同一呼叫ID的挂断都找不到会话并且不发送任何内容。

CallEventListener始终运行(真实部署始终具有共置FreeSWITCH;仅在 test_mode 下跳过)。它 由单独的开关控制 — 是否发送CCR由 online_charging.enabled 决定。关闭计费时,监听器仍然接收并记录 CHANNEL_ANSWER/CHANNEL_HANGUP_COMPLETE 作为调试,但不发送CCR。(早期版本有一个单独的 esl_notifications 切换;它被移除,因为它必须与 online_charging.enabled 一致,并且在计费开启但切换缺失时默默破坏了计费 — 仅CCR-Initial,没有CCR-U/CCR-T — 当计费开启但切换缺失时。)

已弃用:/call_event HTTP端点

早期版本通过FreeSWITCH拨号计划的 curl 钩子检测应答/挂断,这些钩子POST到OmniTAS上的 /call_event HTTP端点。此机制已弃用。 拨号计划钩子已被移除,ESL现在是通知源。

/call_event 端点仍然存在并返回 200 OK,以便任何残留的拨号计划引用不会出错,但它 不执行任何计费操作。升级自旧版本的操��员应确保他们的拨号计划不再依赖于它。

Diameter消息

CCR-Initial (请求类型1)

在呼叫设置之前发送以请求授权和初始信用分配。

发送的关键AVPs:

AVP代码类型描述
Session-Id263UTF8String会话标识符,在整个信用控制会话中保持不变(CCR-I/U/T共享)。根据SIP呼叫ID确定。
Auth-Application-Id258Unsigned32根据RFC 4006 的Diameter信用控制应用程序的值为 4
Service-Context-Id461UTF8String"000.000.12.32260@3gpp.org" 用于IMS计费,参见 TS 32.299
CC-Request-Type416枚举值为 1 (INITIAL_REQUEST)
CC-Request-Number415Unsigned32序列号 — 0 为INITIAL_REQUEST,然后为会话中的后续请求 12,…
Service-Identifier439Unsigned32稳定的预配置服务标识符(service_identifier 配置)
Subscription-Id443分组订阅者MSISDN或IMSI
Requested-Service-Unit437分组预留请求 — CC-Time = requested_units_seconds
Service-Information873分组IMS特定的呼叫详细��息(呼叫方/被叫方、节点角色、时间戳)

示例CCR-I:

Session-Id: "tas01.example.org;1463927445;1744753804"
Auth-Application-Id: 4
CC-Request-Type: 1 (INITIAL_REQUEST)
CC-Request-Number: 0
Subscription-Id:
- Subscription-ID-Type: 0 (END_USER_E164)
Subscription-ID-Data: "313380000000670"
Multiple-Services-Credit-Control:
- Service-Identifier: 1
Requested-Service-Unit:
- CC-Time: 300 (请求的预留)
Service-Information:
- IMS-Information:
- Role-Of-Node: 0 (ORIGINATING_ROLE)
- Node-Functionality: 6 (AS)
- Calling-Party-Address: "tel:+313380000000670"
- Called-Party-Address: "tel:+24724741234"

Requested-Service-Unit 携带真实的预留(不是零)。OCS决定实际授予,并在CCA的 Granted-Service-Unit 中返回。

CCA (信用控制应答)

来自OCS的响应,包含授权决定和授予的信用。

接收的关键AVPs:

AVP代码类型描述
Result-Code268Unsigned322001 表示成功。有关错误值,请参见 结果代码
Granted-Service-Unit431分组分配的信用(以秒为单位的时间)
Service-Information873分组附加计费数据(运营商信息、收费方等)

示例CCA与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分钟)
Service-Information:
- IMS-Information:
- Carrier-Select-Routing-Information: "1408"
- Alternate-Charged-Party-Address: "NickTest"

结果变量:

allocated_time = 600
CCA.Service-Information.Carrier-Select-Routing-Information = "1408"
CCA.Service-Information.Alternate-Charged-Party-Address = "NickTest"

CCR-Update (请求类型2)

在活动呼叫期间发送,用于定期重新授权或临时使用报告。

发送时机:

  • 在呼叫应答时(来自ESL的 CHANNEL_ANSWER) — CC-Request-Number = 1,在 SIP-Response-Timestamp 中带有实际应答时间
  • 在活动呼叫期间定期,根据每次授予的 allocated_time - ccr_update_buffer_seconds 动态定时(回退到 periodic_ccr_time_seconds

与CCR-I的关键区别:

  • CC-Request-Type: 2 (UPDATE_REQUEST)
  • CC-Request-Number: 应答时为 1,然后在每次定期重新认证时递增
  • Requested-Service-Unit: 下一个预留(requested_units_seconds
  • 与CCR-Initial相同的常量 Session-Id

消耗的时间在CCR-Terminate中报告(Used-Service-Unit),��不是在临时CCR-Updates中。临时更新延长预留;最终使用情况在终止时结算。

CCR-Terminate (请求类型3)

在挂断时(来自ESL的 CHANNEL_HANGUP_COMPLETE)发送,报告最终使用情况。

关键AVPs:

  • CC-Request-Type: 3 (TERMINATION_REQUEST)
  • CC-Request-Number: 下一个序列号(例如 2
  • Used-Service-Unit: 总消耗通话时间(CC-Time,自应答以来的秒数)
  • Termination-Cause: 1 (DIAMETER_LOGOUT)根据 RFC 6733 §8.15
  • Cause-Code(AVP 861,IMS信息):0 表示正常应答和释放的呼叫,2 表示会话设置不成功,参见 TS 32.299

结果代码

代码名称描述OmniTAS操作
2001DIAMETER_SUCCESS请求批准解析AVPs,设置呼叫
4010DIAMETER_END_USER_SERVICE_DENIED对订阅者拒绝服务拒绝呼叫,原因 CALL_REJECTED
4012DIAMETER_CREDIT_LIMIT_REACHED信用不足拒绝呼叫,原因 OUTGOING_CALL_BARRED
5003DIAMETER_AUTHORIZATION_REJECTEDOCS策略拒绝拒绝呼叫,原因 CALL_REJECTED
5xxx永久性故障OCS配置或系统错误拒绝呼叫,记录错误

参考: RFC 6733 §7.13GPP TS 32.299

指标

请参见 docs/metrics.md 以获取完整的指标目录。以下指标与在线计费相关。

Diameter请求/响应指标

CCRs通过 command 标签分开,因此可以独立跟踪CCR-Initial(设置)、CCR-Update(临时重新认证)和CCR-Terminate(拆除) — 然后可以单独看到失败的临时更新的激增。

指标: diameter_requests_total (计数器) — 发送的Diameter请求 指标: diameter_responses_total (计数器) — 接收到的Diameter响应 指标: diameter_response_duration_milliseconds (直方图) — 请求往返时间

标签:

  • application - ro (在线计费) 或 sh (订阅者数据)
  • command - ccr_iccr_uccr_t (Ro) 或 udr (Sh)
  • result_code - (仅响应) Diameter结果代码: 20014012 等; 0 = 超时 / 无法解析的回复
  • result - (仅持续时间) successnocrediterror

示例查询:

# 按请求类型的CCR速率
sum by (command) (rate(diameter_requests_total{application="ro"}[5m]))

# 按请求类型的CCR错误率(任何不是2001的)
sum by (command) (rate(diameter_responses_total{application="ro", result_code!="2001"}[5m]))

# 信用限制拒绝(4012)
rate(diameter_responses_total{application="ro", result_code="4012"}[5m])

# 按请求类型的95百分位CCR延迟
histogram_quantile(0.95,
sum by (le, command) (rate(diameter_response_duration_milliseconds_bucket{application="ro"}[5m]))
)

信用控制配额指标

指标: ro_charging_quota_seconds 类型: 直方图 描述: 在每个CCR中观察到的配额(以秒为单位),用于验证计费的正确性 标签:

  • request_type - ccr_iccr_uccr_t
  • kind - requested(请求的CC时间),granted(授予的服务单位,0 = 无信用),used(在终止时使用的服务单位)

示例查询:

# 呼叫设置时授予配额的中位数
histogram_quantile(0.5, rate(ro_charging_quota_seconds_bucket{request_type="ccr_i", kind="granted"}[5m]))

# 无信用授予(零秒分配)每秒
rate(ro_charging_quota_seconds_bucket{kind="granted", le="0"}[5m])

# 每秒报告的终止时使用的总秒数(与计费分钟的合理性检查)
rate(ro_charging_quota_seconds_sum{kind="used"}[5m])

OCS授权和事件指标

指标: ocs_authorization_attempts_total (计数器) 标签: result (successnocredittimeouterror),skipped (true 如果通过正则表达式绕过,否则为 false)

指标: online_charging_events_total (计数器) — 生命周期事件 标签: event_type (authorizeanswerreauthhangupcredit_exhaustion_hanguphangup_rescheduled),result (successnocredittimeouterrortriggered)

示例查询:

# 授权成功率(不包括跳过的)
sum(rate(ocs_authorization_attempts_total{result="success", skipped="false"}[5m]))
/ sum(rate(ocs_authorization_attempts_total{skipped="false"}[5m]))

# 由于信用耗尽而中途释放的呼叫
rate(online_charging_events_total{event_type="credit_exhaustion_hangup"}[5m])

故障排除

FreeSWITCH中不可用的AVP变量

症状:

  • FreeSWITCH拨号计划无法访问 ${CCA.Service-Information.*} 变量
  • 变量显示为空或未定义

可能的原因:

  1. OCS未在CCA中返回服务信息AVPs
  2. AVP解析因意外结构失败
  3. 变量未导出到FreeSWITCH通道

解决方案:

  1. 验证OCS响应包含AVPs

    检查OmniTAS日志中的CCA消息:

    [debug] 信用控制应答: {:diameter_packet, ...}
    [debug] 解析的AVP变量: %{
    "CCA.Service-Information.Carrier-Select-Routing-Information" => "1408",
    "CCA.Service-Information.Alternate-Charged-Party-Address" => "NickTest"
    }

    如果“解析的AVP变量”为空 %{},则OCS未返回预期的AVPs。

  2. 检查AVP解析错误

    查找日志中的警告:

    [warning] 收到另一种类型的回复: {...}

    这表明AVP结构与预期格式不匹配。检查Diameter数据包结构。

  3. 验证FreeSWITCH变量导出

    在FreeSWITCH控制台或ESL中:

    freeswitch> uuid_dump <call-uuid>

    查找带有 variable_ 前缀和 CCA. 的变量:

    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

    注意: FreeSWITCH保留变量名称中的点和连字符。它们在拨号计划中正常工作:

    <action application="log" data="运营商: ${CCA.Service-Information.Carrier-Select-Routing-Information}"/>

呼叫被拒绝,错误为“unhandled”

症状:

  • 日志显示: [warning] 无法授权呼叫: :unhandled
  • 有效的CCA响应(结果代码2001)被拒绝
  • 尽管OCS批准,但呼叫失败

可能的原因:

  • CCA消息结构与预期模式不匹配
  • 供应商特定AVPs在意外位置
  • AVP位置索引不匹配

解决方案:

这���已在最近版本中修复的已知问题。确保您正在运行当前版本。

先前的行为: 模式匹配要求:

  • 授予服务单位AVP在位置7
  • 空的供应商特定AVP列表 []

当前行为: 模式匹配接受:

  • 授予服务单位AVP在任何位置
  • 非空的供应商特定AVP列表

如果问题仍然存在:

  1. 捕获日志中的CCA数据包结构
  2. 检查AVPs是否符合预期的Diameter格式
  3. 验证结果代码是否为2001

OCS对所有请求超时

症状:

  • 所有CCR请求超时
  • 日志显示: [debug] 收到授权响应: {:error, :timeout}
  • 在5秒内未收到CCA

可能的原因:

  • 与OCS/DRA的网络连接问题
  • 防火墙阻止Diameter端口(3868)
  • destination_realmdestination_host 不正确
  • OCS未对请求做出响应

解决方案:

  1. 验证网络连接

    测试与OCS的TCP连接:

    telnet ocs.example.com 3868

    应成功连接。如果连接被拒绝或超时,请检查防火墙规则。

  2. 检查Diameter配置

    验证 destination_realm 是否与OCS配置匹配:

    config :tas, :diameter,
    destination_realm: "epc.mnc001.mcc001.3gppnetwork.org" # 必须与OCS领域匹配
  3. 查看OCS日志

    检查OCS是否接收到CCR消息。如果OCS接收到请求但未响应:

    • 验证OmniTAS的 origin_host 是否被OCS识别
    • 检查OCS对等体配置是否允许来自OmniTAS的连接
    • 验证服务上下文ID和应用程序ID是否与OCS预期匹配

信用耗尽未挂断呼叫

症状:

  • 呼叫在授予的信用时间后继续
  • allocated_time 到期时未自动挂断
  • 启用了 schedule_hangup_auth 但未生效

可能的原因:

  • FreeSWITCH计划挂断未配置
  • schedule_hangup_authfalse
  • 呼叫状态未正确跟踪

解决方案:

  1. 验证配置

    确保启用了 schedule_hangup_auth

    config :tas, :online_charging,
    schedule_hangup_auth: true
  2. 检查FreeSWITCH ESL连接

    验证OmniTAS是否可以向FreeSWITCH发送命令:

    [debug] 调度挂断响应: {:ok, "+OK"}

    如果出现错误或没有响应,请检查FreeSWITCH事件套接字配置。

  3. 监控呼叫状态

    检查呼叫UUID是否在呼叫状态中被跟踪:

    [debug] 为呼叫设置计划挂断,600秒后

    如果找不到UUID,呼叫状态跟踪可能存在问题。

跳过的正则表达式未绕过OCS

症状:

  • 紧急呼叫(911,000)仍然经过OCS授权
  • 匹配 skipped_regex 模式的号码未被绕过
  • 紧急呼���延迟

可能的原因:

  • 正则表达式模式语法错误
  • 目标号码格式不匹配
  • 正则表达式未正确转义

解决方案:

  1. 验证正则表达式模式

    测试正则表达式编译:

    Regex.compile("^911$")  # 应返回 {:ok, ~r/^911$/}

    常见错误:

    • 缺少锚点:使用 ^911$ 而不是 911
    • 转义:使用 \* 表示字面星号,而不是 \*
  2. 检查号码格式

    验证拨打的号码格式是否与模式匹配:

    [debug] 检查拨打的号码 "911" 是否匹配跳过的正则表达式...

    如果号码格式为 "+1911",但模式为 "^911$",则不会匹配。

  3. 示例模式

    config :tas, :online_charging,
    skipped_regex: [
    "^911$", # 美国紧急
    "^000$", # 澳大利亚紧急
    "^112$", # 国际紧急
    "^\*86$", # 语音信箱(转义星号)
    "^1?800\d{7}$" # 免费电话
    ]

参考

3GPP规范

规范标题相关章节
TS 32.299Diameter计费应用§6.3 (Ro接口),§7.2 (AVP定义)
TS 32.240计费架��和原则§5 (在线计费)
TS 29.229Cx和Dx接口IMS中服务信息AVP的使用

IETF RFCs

RFC标题相关章节
RFC 6733Diameter基本协议§3 (协议概述),§7 (错误处理)
RFC 4006Diameter信用控制应用§8 (信用控制消息)

AVP代码参考

供应商ID 0 = IETF基础(RFC 6733 / RFC 4006);供应商ID 10415 = 3GPP (TS 32.299)。

完整的CCR结构(语音呼叫)

OmniTAS在呼叫信用控制请求中包含的每个AVP,显示分组:

信用控制请求 (命令 272, 应用 4)
├─ Session-Id (263)
├─ Origin-Host (264)
├─ Origin-Realm (296)
├─ Destination-Realm (283)
├─ Destination-Host (293) [仅在配置时]
├─ Auth-Application-Id (258) = 4
├─ Service-Context-Id (461) = 000.000.12.32260@3gpp.org
├─ CC-Request-Type (416)
├─ CC-Request-Number (415)
├─ Event-Timestamp (55)
├─ User-Name (1) [仅在设置用户名时]
├─ Termination-Cause (295) [仅CCR-T]
├─ Subscription-Id (443)
│ ├─ Subscription-Id-Type (450) = 0 (END_USER_E164)
│ └─ Subscription-Id-Data (444) = 订阅者MSISDN
├─ Multiple-Services-Credit-Control (456)
│ ├─ Service-Identifier (439)
│ ├─ Requested-Service-Unit (437) [CCR-I, CCR-U]
│ │ └─ CC-Time (420)
│ └─ Used-Service-Unit (446) [CCR-T]
│ └─ CC-Time (420)
└─ Service-Information (873, v10415)
├─ IN-Information [运营商扩展]
│ └─ Real-Called-Number
└─ IMS-Information (876, v10415)
├─ Role-Of-Node (829) = 0 (ORIGINATING_ROLE)
├─ Node-Functionality (862) = 6 (AS)
├─ User-Session-Id (830) = SIP呼叫ID
├─ Calling-Party-Address (831)
├─ Called-Party-Address (832)
├─ Requested-Party-Address (1251)
├─ Time-Stamps (833)
│ ├─ SIP-Request-Timestamp (834)
│ ├─ SIP-Request-Timestamp-Fraction (2301)
│ ├─ SIP-Response-Timestamp (835) [应答后]
│ └─ SIP-Response-Timestamp-Fraction (2302) [应答后]
└─ Cause-Code (861) [CCR-T]

OmniTAS发送的AVPs (CCR)

In 列显示请求类型携带AVP: I = CCR-Initial, U = CCR-Update, T = CCR-Terminate.

AVP代码供应商类型值 / 源
Session-Id2630UTF8StringI U T每个呼叫的常量(从SIP呼叫ID派生);在I/U/T中相同
Origin-Host2640DiameterIdentityI U TOmniTAS Diameter身份(origin_host.origin_realm
Origin-Realm2960DiameterIdentityI U Torigin_realm
Destination-Realm2830DiameterIdentityI U TOCS领域
Destination-Host2930DiameterIdentityI U T仅在配置特定OCS主机时存在
Auth-Application-Id2580Unsigned32I U T4 (Diameter信用控制应用)
Service-Context-Id4610UTF8StringI U T000.000.12.32260@3gpp.org
CC-Request-Type4160枚举I U T1=初始,2=更新,3=终止
CC-Request-Number4150Unsigned32I U T0 为初始,然后为 12,…
Event-Timestamp550时间I U T请求生成的时间
User-Name10UTF8StringI U T可选;仅在提供用户名/IMSI时
Termination-Cause2950枚举T1 (DIAMETER_LOGOUT)
Subscription-Id4430分组I U T订阅者身份(见子项)
→ Subscription-Id-Type4500枚举I U T0 (END_USER_E164)
→ Subscription-Id-Data4440UTF8StringI U T订阅者MSISDN
Multiple-Services-Credit-Control4560分组I U T信用控制容器(见子项)
→ Service-Identifier4390Unsigned32I U Tservice_identifier (稳定,预配置)
→ Requested-Service-Unit4370分组I U预留请求
→ → CC-Time4200Unsigned32I Urequested_units_seconds
→ Used-Service-Unit4460分组T最终消费
→ → CC-Time4200Unsigned32T自应答以来消耗的秒数
Service-Information87310415分组I U T3GPP服务容器
→ IN-Information10415分组I U T运营商扩展,携带实际拨打的号码
→ → Real-Called-Number10415UTF8StringI U T拨打(被叫)号码
→ IMS-Information87610415分组I U TIMS计费详细信息
→ → Role-Of-Node82910415枚举I U T0 (ORIGINATING_ROLE) — MO是收费腿
→ → Node-Functionality86210415枚举I U T6 (AS)
→ → User-Session-Id83010415UTF8StringI U TSIP呼叫ID(OCS关联键)
→ → Calling-Party-Address83110415UTF8StringI U Ttel:+<calling MSISDN>
→ → Called-Party-Address83210415UTF8StringI U Ttel:+<called number>
→ → Requested-Party-Address125110415UTF8StringI U Ttel:+<called number>
→ → Time-Stamps83310415分组I U TSIP请求/响应时间
→ → → SIP-Request-Timestamp83410415时间I U TINVITE时间(整秒)
→ → → SIP-Request-Timestamp-Fraction230110415Unsigned32I U TINVITE时间(毫秒)
→ → → SIP-Response-Timestamp83510415时间U T应答(200 OK)时间 — 仅在应答时
→ → → SIP-Response-Timestamp-Fraction230210415Unsigned32U T应答时间(毫秒) — 仅在应答时
→ → Cause-Code86110415Integer32T0 正常释放 / 2 不成功的设置

代码显示为 的是运营商/供应商扩展,携带在 Service-Information 下;它们没有3GPP分配的代码,并在OmniTAS Diameter字典中定义。

从OCS接收的AVPs (CCA)

AVP代码供应商类型描述
Result-Code2680Unsigned322001 = 成功;请参见 结果代码
Granted-Service-Unit4310分组分配的信用
→ CC-Time4200Unsigned32授予的秒数(allocated_time)。0 ⇒ 无信用 ⇒ 禁止/挂断
Service-Information87310415分组OCS返回的可选计费数据
→ Carrier-Select-Routing-Information202310415UTF8String运营商路由代码(映射到FreeSWITCH变量)
→ Alternate-Charged-Party-Address128010415UTF8String计费方标识符(映射到FreeSWITCH变量)

所有在CCA中返回的 Service-Information 子AVPs被扁平化为FreeSWITCH通道变量 — 见 AVP解析和变量映射FreeSWITCH通道变量