在线计费系统 (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)拨号计划时进行评估:
- 在线计费已启用 —
online_charging.enabled为true。当为false时,OmniTAS会在本地授权每个呼叫,并且从不联系OCS。 - 呼叫是移动发起 (MO) — 只有MO腿会收费。移动终止 (MT) 呼叫永远不会收费:MT路径不执行OCS授权,因此不会为其创建信用控制会话。
- 目的地不在豁免名单中 — 拨打的号码不匹配
skipped_regex中的任何模式(例如紧急号码、服务代码)。
如果这三条均成立,OmniTAS将发送 CCR-Initial 并打开一个以SIP Call-ID为键的信用控制会话。从那时起,CCR-Update 和 CCR-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 拨号计划,该计划播放公告然后挂断
转移的工作原理:
- OmniTAS设置
tas_call_reason=credit_exhausted通道变量 - 调度转移到
ims_as拨号计划上下文中的credit_exhausted分机 - 当定时器触发时:
- 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提供两种互补机制:
-
计划定时器 (
schedule_hangup_auth):- 当授予的信用到期时自动挂断/转移
- 在每个CCR-U响应时动态重新调度
- 使用缓冲逻辑在到期之前发送CCR-U
- 与公告功能集成
-
即时耗尽处理:
- 当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处理逻辑
处理规则:
- 分组AVPs为变量名称层次结构添加一层,但本身没有值
- 简单AVPs被映射到具有其完整点路径的变量
- 供应商特定AVPs与标准AVPs的处理方式相同
- 未知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 | 布尔 | 否 | false | 3GPP 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 # 基于领域的路由
工作原理:
当接收到呼叫时:
- 目标号码与
skipped_regex模式进行检查 - 如果匹配,呼叫绕过OCS(对于紧急服务非常有用)
- 如果不匹配,CCR-Initial发送到OCS的
destination_realm - 解析CCA响应以获取授予的单位和AVPs
- 将AVPs映射到FreeSWITCH变量(见 AVP映射)
- 呼叫继续,
allocated_time和AVP数据可用 - 在呼叫期间每
periodic_ccr_time_seconds发送CCR-Update - 如果启用了
schedule_hangup_auth,则在信用到期时自动挂断 - 在呼叫完成时发送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_ANSWER和CHANNEL_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-Id | 263 | UTF8String | 会话标识符,在整个信用控制会话中保持不变(CCR-I/U/T共享)。根据SIP呼叫ID确定。 |
| Auth-Application-Id | 258 | Unsigned32 | 根据RFC 4006 的Diameter信用控制应用程序的值为 4 |
| Service-Context-Id | 461 | UTF8String | "000.000.12.32260@3gpp.org" 用于IMS计费,参见 TS 32.299 |
| CC-Request-Type | 416 | 枚举 | 值为 1 (INITIAL_REQUEST) |
| CC-Request-Number | 415 | Unsigned32 | 序列号 — 0 为INITIAL_REQUEST,然后为会话中的后续请求 1,2,… |
| Service-Identifier | 439 | Unsigned32 | 稳定的预配置服务标识符(service_identifier 配置) |
| Subscription-Id | 443 | 分组 | 订阅者MSISDN或IMSI |
| Requested-Service-Unit | 437 | 分组 | 预留请求 — CC-Time = requested_units_seconds |
| Service-Information | 873 | 分组 | 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-Code | 268 | Unsigned32 | 2001 表示成功。有关错误值,请参见 结果代码。 |
| Granted-Service-Unit | 431 | 分组 | 分配的信用(以秒为单位的时间) |
| Service-Information | 873 | 分组 | 附加计费数据(运营商信息、收费方等) |
示例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.15Cause-Code(AVP 861,IMS信息):0表示正常应答和释放的呼叫,2表示会话设置不成功,参见 TS 32.299
结果代码
| 代码 | 名称 | 描述 | OmniTAS操作 |
|---|---|---|---|
| 2001 | DIAMETER_SUCCESS | 请求批准 | 解析AVPs,设置呼叫 |
| 4010 | DIAMETER_END_USER_SERVICE_DENIED | 对订阅者拒绝服务 | 拒绝呼叫,原因 CALL_REJECTED |
| 4012 | DIAMETER_CREDIT_LIMIT_REACHED | 信用不足 | 拒绝呼叫,原因 OUTGOING_CALL_BARRED |
| 5003 | DIAMETER_AUTHORIZATION_REJECTED | OCS策略拒绝 | 拒绝呼叫,原因 CALL_REJECTED |
| 5xxx | 永久性故障 | OCS配置或系统错误 | 拒绝呼叫,记录错误 |
参考: RFC 6733 §7.1 和 3GPP 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_i,ccr_u,ccr_t(Ro) 或udr(Sh)result_code- (仅响应) Diameter结果代码:2001,4012等;0= 超时 / 无法解析的回复result- (仅持续时间)success,nocredit,error
示例查询:
# 按请求类型的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_i,ccr_u,ccr_tkind-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 (success,nocredit,timeout,error),skipped (true 如果通过正则表达式绕过,否则为 false)
指标: online_charging_events_total (计数器) — 生命周期事件
标签: event_type (authorize,answer,reauth,hangup,credit_exhaustion_hangup,hangup_rescheduled),result (success,nocredit,timeout,error,triggered)
示例查询:
# 授权成功率(不包括跳过的)
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.*}变量 - 变量显示为空或未定义
可能的原因:
- OCS未在CCA中返回服务信息AVPs
- AVP解析因意外结构失败
- 变量未导出到FreeSWITCH通道
解决方案:
-
验证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。 -
检查AVP解析错误
查找日志中的警告:
[warning] 收到另一种类型的回复: {...}这表明AVP结构与预期格式不匹配。检查Diameter数据包结构。
-
验证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列表
如果问题仍然存在:
- 捕获日志中的CCA数据包结构
- 检查AVPs是否符合预期的Diameter格式
- 验证结果代码是否为2001
OCS对所有请求超时
症状:
- 所有CCR请求超时
- 日志显示:
[debug] 收到授权响应: {:error, :timeout} - 在5秒内未收到CCA
可能的原因:
- 与OCS/DRA的网络连接问题
- 防火墙阻止Diameter端口(3868)
destination_realm或destination_host不正确- OCS未对请求做出响应
解决方案:
-
验证网络连接
测试与OCS的TCP连接:
telnet ocs.example.com 3868应成功连接。如果连接被拒绝或超时,请检查防火墙规则。
-
检查Diameter配置
验证
destination_realm是否与OCS配置匹配:config :tas, :diameter,
destination_realm: "epc.mnc001.mcc001.3gppnetwork.org" # 必须与OCS领域匹配 -
查看OCS日志
检查OCS是否接收到CCR消息。如果OCS接收到请求但未响应:
- 验证OmniTAS的
origin_host是否被OCS识别 - 检查OCS对等体配置是否允许来自OmniTAS的连接
- 验证服务上下文ID和应用程序ID是否与OCS预期匹配
- 验证OmniTAS的
信用耗尽未挂断呼叫
症状:
- 呼叫在授予的信用时间后继续
- 当
allocated_time到期时未自动挂断 - 启用了
schedule_hangup_auth但未生效
可能的原因:
- FreeSWITCH计划挂断未配置
schedule_hangup_auth为false- 呼叫状态未正确跟踪
解决方案:
-
验证配置
确保启用了
schedule_hangup_auth:config :tas, :online_charging,
schedule_hangup_auth: true -
检查FreeSWITCH ESL连接
验证OmniTAS是否可以向FreeSWITCH发送命令:
[debug] 调度挂断响应: {:ok, "+OK"}如果出现错误或没有响应,请检查FreeSWITCH事件套接字配置。
-
监控呼叫状态
检查呼叫UUID是否在呼叫状态中被跟踪:
[debug] 为呼叫设置计划挂断,600秒后如果找不到UUID,呼叫状态跟踪可能存在问题。
跳过的正则表达式未绕过OCS
症状:
- 紧急呼叫(911,000)仍然经过OCS授权
- 匹配
skipped_regex模式的号码未被绕过 - 紧急呼���延迟
可能的原因:
- 正则表达式模式语法错误
- 目标号码格式不匹配
- 正则表达式未正确转义
解决方案:
-
验证正则表达式模式
测试正则表达式编译:
Regex.compile("^911$") # 应返回 {:ok, ~r/^911$/}常见错误:
- 缺少锚点:使用
^911$而不是911 - 转义:使用
\*表示字面星号,而不是\*
- 缺少锚点:使用
-
检查号码格式
验证拨打的号码格式是否与模式匹配:
[debug] 检查拨打的号码 "911" 是否匹配跳过的正则表达式...如果号码格式为 "+1911",但模式为
"^911$",则不会匹配。 -
示例模式
config :tas, :online_charging,
skipped_regex: [
"^911$", # 美国紧急
"^000$", # 澳大利亚紧急
"^112$", # 国际紧急
"^\*86$", # 语音信箱(转义星号)
"^1?800\d{7}$" # 免费电话
]
参考
3GPP规范
| 规范 | 标题 | 相关章节 |
|---|---|---|
| TS 32.299 | Diameter计费应用 | §6.3 (Ro接口),§7.2 (AVP定义) |
| TS 32.240 | 计费架��和原则 | §5 (在线计费) |
| TS 29.229 | Cx和Dx接口 | IMS中服务信息AVP的使用 |
IETF RFCs
| RFC | 标题 | 相关章节 |
|---|---|---|
| RFC 6733 | Diameter基本协议 | §3 (协议概述),§7 (错误处理) |
| RFC 4006 | Diameter信用控制应用 | §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-Id | 263 | 0 | UTF8String | I U T | 每个呼叫的常量(从SIP呼叫ID派生);在I/U/T中相同 |
| Origin-Host | 264 | 0 | DiameterIdentity | I U T | OmniTAS Diameter身份(origin_host.origin_realm) |
| Origin-Realm | 296 | 0 | DiameterIdentity | I U T | origin_realm |
| Destination-Realm | 283 | 0 | DiameterIdentity | I U T | OCS领域 |
| Destination-Host | 293 | 0 | DiameterIdentity | I U T | 仅在配置特定OCS主机时存在 |
| Auth-Application-Id | 258 | 0 | Unsigned32 | I U T | 4 (Diameter信用控制应用) |
| Service-Context-Id | 461 | 0 | UTF8String | I U T | 000.000.12.32260@3gpp.org |
| CC-Request-Type | 416 | 0 | 枚举 | I U T | 1=初始,2=更新,3=终止 |
| CC-Request-Number | 415 | 0 | Unsigned32 | I U T | 0 为初始,然后为 1,2,… |
| Event-Timestamp | 55 | 0 | 时间 | I U T | 请求生成的时间 |
| User-Name | 1 | 0 | UTF8String | I U T | 可选;仅在提供用户名/IMSI时 |
| Termination-Cause | 295 | 0 | 枚举 | T | 1 (DIAMETER_LOGOUT) |
| Subscription-Id | 443 | 0 | 分组 | I U T | 订阅者身份(见子项) |
| → Subscription-Id-Type | 450 | 0 | 枚举 | I U T | 0 (END_USER_E164) |
| → Subscription-Id-Data | 444 | 0 | UTF8String | I U T | 订阅者MSISDN |
| Multiple-Services-Credit-Control | 456 | 0 | 分组 | I U T | 信用控制容器(见子项) |
| → Service-Identifier | 439 | 0 | Unsigned32 | I U T | service_identifier (稳定,预配置) |
| → Requested-Service-Unit | 437 | 0 | 分组 | I U | 预留请求 |
| → → CC-Time | 420 | 0 | Unsigned32 | I U | requested_units_seconds |
| → Used-Service-Unit | 446 | 0 | 分组 | T | 最终消费 |
| → → CC-Time | 420 | 0 | Unsigned32 | T | 自应答以来消耗的秒数 |
| Service-Information | 873 | 10415 | 分组 | I U T | 3GPP服务容器 |
| → IN-Information | — | 10415 | 分组 | I U T | 运营商扩展,携带实际拨打的号码 |
| → → Real-Called-Number | — | 10415 | UTF8String | I U T | 拨打(被叫)号码 |
| → IMS-Information | 876 | 10415 | 分组 | I U T | IMS计费详细信息 |
| → → Role-Of-Node | 829 | 10415 | 枚举 | I U T | 0 (ORIGINATING_ROLE) — MO是收费腿 |
| → → Node-Functionality | 862 | 10415 | 枚举 | I U T | 6 (AS) |
| → → User-Session-Id | 830 | 10415 | UTF8String | I U T | SIP呼叫ID(OCS关联键) |
| → → Calling-Party-Address | 831 | 10415 | UTF8String | I U T | tel:+<calling MSISDN> |
| → → Called-Party-Address | 832 | 10415 | UTF8String | I U T | tel:+<called number> |
| → → Requested-Party-Address | 1251 | 10415 | UTF8String | I U T | tel:+<called number> |
| → → Time-Stamps | 833 | 10415 | 分组 | I U T | SIP请求/响应时间 |
| → → → SIP-Request-Timestamp | 834 | 10415 | 时间 | I U T | INVITE时间(整秒) |
| → → → SIP-Request-Timestamp-Fraction | 2301 | 10415 | Unsigned32 | I U T | INVITE时间(毫秒) |
| → → → SIP-Response-Timestamp | 835 | 10415 | 时间 | U T | 应答(200 OK)时间 — 仅在应答时 |
| → → → SIP-Response-Timestamp-Fraction | 2302 | 10415 | Unsigned32 | U T | 应答时间(毫秒) — 仅在应答时 |
| → → Cause-Code | 861 | 10415 | Integer32 | T | 0 正常释放 / 2 不成功的设置 |
代码显示为
—的是运营商/供应商扩展,携带在Service-Information下;它们没有3GPP分配的代码,并在OmniTAS Diameter字典中定义。
从OCS接收的AVPs (CCA)
| AVP | 代码 | 供应商 | 类型 | 描述 |
|---|---|---|---|---|
| Result-Code | 268 | 0 | Unsigned32 | 2001 = 成功;请参见 结果代码 |
| Granted-Service-Unit | 431 | 0 | 分组 | 分配的信用 |
| → CC-Time | 420 | 0 | Unsigned32 | 授予的秒数(allocated_time)。0 ⇒ 无信用 ⇒ 禁止/挂断 |
| Service-Information | 873 | 10415 | 分组 | OCS返回的可选计费数据 |
| → Carrier-Select-Routing-Information | 2023 | 10415 | UTF8String | 运营商路由代码(映射到FreeSWITCH变量) |
| → Alternate-Charged-Party-Address | 1280 | 10415 | UTF8String | 计费方标识符(映射到FreeSWITCH变量) |
所有在CCA中返回的 Service-Information 子AVPs被扁平化为FreeSWITCH通道变量 — 见 AVP解析和变量映射 和 FreeSWITCH通道变量。