Guia de Operações do OmniCHF
Índice
- Visão Geral do Componente
- Referências de Papel e Especificação 3GPP
- Endpoints SBI
- Referência de Configuração
- Procedimentos Chave
- Observabilidade
- Limitações Conhecidas
- Solução de Problemas
Visão Geral do Componente
OmniCHF implementa a função de rede de Função de Cobrança (CHF) definida na 3GPP TS 32.291. O CHF fornece cobrança online e offline convergente para sessões PDU 5G via o serviço Nchf_ConvergedCharging. Ele traduz solicitações de cobrança 5G em chamadas JSON-RPC SessionS CGRateS para autorização de crédito e gerenciamento de sessão, e gera Registros de Detalhes de Chamadas (CDRs) na liberação da sessão.
Ciclo de Vida da Sessão de Cobrança
Cada sessão PDU mapeia para uma sessão de cobrança, rastreada por um chargingDataRef (UUID). O estado da sessão é mantido em um Agente em memória e não é persistido. Um reinício perde todo o estado da sessão ativa.
| Estado | Gatilho | Ação de armazenamento |
|---|---|---|
| Criado | POST /chargingdata | Contexto criado, CGRateS InitiateSession chamado |
| Atualizado | POST /chargingdata/:ref/update | Contexto atualizado (uso acumulado, sequência incrementada) |
| Liberado | POST /chargingdata/:ref/release | CDR construído e registrado, CGRateS TerminateSession chamado, contexto excluído |
Referências de Papel e Especificação 3GPP
| Item | Referência |
|---|---|
| Definição de NF CHF | 3GPP TS 23.501 Seção 6.2.16 |
| Serviço Nchf_ConvergedCharging | 3GPP TS 32.291 |
| Procedimento de Criação de Dados de Cobrança | 3GPP TS 32.291 Seção 6.1.3.2.1 |
| Procedimento de Atualização de Dados de Cobrança | 3GPP TS 32.291 Seção 6.1.3.2.2 |
| Procedimento de Liberação de Dados de Cobrança | 3GPP TS 32.291 Seção 6.1.3.2.3 |
| Modelo de dados ChargingDataRequest / Response | 3GPP TS 32.291 Seção 6.1.6 |
| Formato CDR para sessões PDU 5G | 3GPP TS 32.290 |
| Estrutura comum do SBI | 3GPP TS 29.500 |
| Registro de NF com NRF | 3GPP TS 29.510 |
Endpoints SBI
Caminho base: /nchf-convergedcharging/v3
| Método | Caminho | Descrição | Sucesso | Erro |
|---|---|---|---|---|
POST | /chargingdata | Criar uma sessão de cobrança (Solicitação inicial). Aloca um chargingDataRef, inicia uma sessão CGRateS e retorna unidades concedidas. | 201 Criado com cabeçalho Location | 500 Erro Interno do Servidor |
POST | /chargingdata/{chargingDataRef}/update | Atualizar uma sessão de cobrança (Solicitação intermediária). Relata o uso atual e solicita crédito adicional. | 200 OK | 404 Não Encontrado, 500 Erro Interno do Servidor |
POST | /chargingdata/{chargingDataRef}/release | Liberar uma sessão de cobrança (Solicitação final). Relata o uso final, gera CDR, termina a sessão CGRateS. | 204 Sem Conteúdo | 404 Não Encontrado, 500 Erro Interno do Servidor |
ChargingDataRequest — Campos Chave
| Campo | Tipo | Usado em | Descrição |
|---|---|---|---|
subscriberIdentifier | string | Criar, Atualizar, Liberar | SUPI (por exemplo, imsi-999700000000001). Usado como identificador de conta CGRateS. |
nfConsumerIdentification | objeto | Criar | Informações do consumidor NF. Fonte de fallback para SUPI se subscriberIdentifier estiver ausente. |
pDUSessionChargingInformation | objeto | Criar, Atualizar, Liberar | Detalhes da sessão PDU: DNN, S-NSSAI, tipo RAT, QoS, ID e tipo da sessão PDU. |
multipleUnitUsage | array | Atualizar, Liberar | Contêineres de uso relatados. O usedUnitContainer do primeiro elemento é usado para extração de volume e duração. |
requestType | string | Todos | INITIAL_REQUEST, UPDATE_REQUEST ou TERMINATION_REQUEST. |
ChargingDataResponse — Campos Chave
| Campo | Tipo | Presente | Descrição |
|---|---|---|---|
invocationSequenceNumber | integer | Criar, Atualizar | Número de sequência para esta resposta. Codificado como 1 na Criação (veja CHF-M1). Incrementado em cada Atualização. |
invocationResult | objeto | Criar, Atualizar | Sempre {"resultCode": "SUCCESS"} no caminho feliz. |
sessionId | string | Criar, Atualizar | O chargingDataRef (UUID) alocado para esta sessão. |
multipleUnitInformation | array | Criar, Atualizar | Unidades concedidas. Contém uma entrada com resultCode, grantedUnit (totalVolume, time) e ratingGroup (codificado como 1, veja CHF-L2). |
Referência de Configuração
Todos os parâmetros são definidos via o ambiente da aplicação (tipicamente config/runtime.exs).
config :omnichf,
sbi_scheme: "http",
sbi_addr: "127.0.0.14",
sbi_port: 7777,
nrf_uri: "http://127.0.0.10:7777",
mcc: "999",
mnc: "70",
heartbeat_interval: 10_000,
cgrates_enabled: false,
cgrates_url: "http://localhost:2080/jsonrpc",
cgrates_tenant: "cgrates.org",
cgrates_timeout: 5000
Tabela de Parâmetros
| Parâmetro | Padrão | Tipo | Descrição |
|---|---|---|---|
sbi_scheme | "http" | string | Esquema de transporte para o ouvinte SBI. |
sbi_addr | "127.0.0.14" | string | Endereço IP ao qual o servidor HTTP SBI se vincula. |
sbi_port | 7777 | integer | Porta TCP na qual o servidor HTTP SBI escuta. |
nrf_uri | "http://127.0.0.10:7777" | string | URI base do NRF usado para registro de NF e heartbeat. |
mcc | "999" | string | Código do País Móvel. Usado no perfil NF enviado ao NRF e no nome da rede de serviço. |
mnc | "70" | string | Código da Rede Móvel. Usado no perfil NF enviado ao NRF e no nome da rede de serviço. |
heartbeat_interval | 10_000 | integer (ms) | Intervalo entre solicitações de heartbeat do NRF. |
cgrates_enabled | false | boolean | Interruptor mestre para integração CGRateS. Quando false, todas as chamadas CGRateS são ignoradas e uma concessão padrão de 86.400 unidades é retornada. Defina como true em produção quando uma instância CGRateS estiver disponível. |
cgrates_url | "http://localhost:2080/jsonrpc" | string | URL do endpoint JSON-RPC para a instância CGRateS. Usado apenas quando cgrates_enabled é true. |
cgrates_tenant | "cgrates.org" | string | Nome do inquilino CGRateS. Passado em todas as chamadas da API SessionS como o campo Tenant. Deve corresponder ao inquilino configurado no CGRateS. |
cgrates_timeout | 5000 | integer (ms) | Tempo limite da solicitação HTTP para chamadas JSON-RPC do CGRateS. Chamadas de verificação de saúde usam min(cgrates_timeout, 3000) para evitar bloquear o endpoint de saúde. |
Notas de Integração do CGRateS
Quando cgrates_enabled é false, o OmniCHF opera em modo de bypass: todas as solicitações de cobrança são aceitas e concedidas 86.400 unidades (tempo ou volume) sem qualquer classificação ou autorização. Isso é adequado para testes em laboratório e integração quando o CGRateS não está disponível.
A comunicação com o CGRateS utiliza o cliente HTTP embutido :httpc do Erlang (veja a limitação CHF-M5). Este cliente não suporta pooling de conexão. Sob alta carga, cada solicitação CGRateS abre e fecha uma nova conexão HTTP, o que pode se tornar um gargalo.
Procedimentos Chave
Criar Sessão de Cobrança (Inicial)
Atualizar Sessão de Cobrança (Intermediária)
Liberar Sessão de Cobrança (Final)
Mapeamento de Eventos do CGRateS
OmniCHF mapeia campos de cobrança 5G para campos de eventos SessionS do CGRateS da seguinte forma:
| Campo CGRateS | Fonte | Notas |
|---|---|---|
Account | subscriberIdentifier (SUPI) | Reverte para nfConsumerIdentification.supi |
Subject | subscriberIdentifier (SUPI) | O mesmo que Account |
Destination | pduSessionInformation.dnnId ou .dnn | Nome da Rede de Dados |
ToR | pduSessionInformation.pduType | Sempre *data para sessões PDU |
RequestType | requestType | Sempre mapeado para *prepaid |
Usage | usedUnitContainer.totalVolume ou soma de uplink+downlink ou time | O primeiro valor não zero vence |
OriginID | chargingDataRef | UUID único por sessão |
OriginHost | estático "OmniCHF" | |
SUPI | subscriberIdentifier | Campo de extensão 5G |
DNN | pduSessionInformation.dnnId | Campo de extensão 5G |
S-NSSAI_SST | pduSessionInformation.sNSSAI.sst | Campo de extensão 5G |
S-NSSAI_SD | pduSessionInformation.sNSSAI.sd | Campo de extensão 5G |
5QI | pduSessionInformation.qoSInformation.5qI | Campo de extensão 5G |
RATType | pduSessionInformation.ratType | Padrão: "NR" |
PDUSessionID | pduSessionInformation.pduSessionID | Campo de extensão 5G |
PDUSessionType | pduSessionInformation.pduType | Padrão: "IPV4" |
Campos CDR
Os CDRs são construídos na liberação da sessão e emitidos para o log da aplicação no nível INFO. O mapa CDR contém:
| Campo | Fonte |
|---|---|
record_type | Estático: "5G_PDU_SESSION" |
supi | Contexto de cobrança |
dnn | pduSessionInformation.dnnId ou .dnn |
snssai | {sst, sd} de pduSessionInformation.sNSSAI |
qos_5qi | pduSessionInformation.qoSInformation.5qI |
rat_type | pduSessionInformation.ratType |
pdu_session_id | Contexto de cobrança |
pdu_session_type | pduSessionInformation.pduType |
volume_uplink | usedUnitContainer.uplinkVolume |
volume_downlink | usedUnitContainer.downlinkVolume |
volume_total | usedUnitContainer.totalVolume (ou uplink+downlink) |
duration | usedUnitContainer.time (ou diferença de relógio se zero) |
start_time | Timestamp created_at da sessão |
end_time | Relógio de parede no momento da liberação |
charging_data_ref | UUID da sessão |
Observabilidade
Eventos de Telemetria
| Evento | Medidas | Tags | Descrição |
|---|---|---|---|
[:omnichf, :charging, :initial] | count | supi | Disparado em cada solicitação de Criação |
[:omnichf, :charging, :update] | count | ref | Disparado em cada solicitação de Atualização |
[:omnichf, :charging, :release] | count | ref | Disparado em cada solicitação de Liberação |
[:omnichf, :charging, :creates] | count | result (success/failure) | Resultado da operação de Criação |
[:omnichf, :charging, :updates] | count | result | Resultado da operação de Atualização |
[:omnichf, :charging, :releases] | count | result | Resultado da operação de Liberação |
[:omnichf, :cgrates, :request] | count, duration_ms | operation, result | Por chamada JSON-RPC do CGRateS |
[:omnichf, :cgrates, :health] | status (1/0) | — | Saúde da conectividade do CGRateS |
[:omnichf, :sessions, :active] | count | — | Medida: sessões de cobrança ativas |
[:omni5g, :nrf, :registration] | status (1/0) | nf_type | Status de registro do NRF |
Métricas do Prometheus
Métricas de Cobrança
| Métrica | Tipo | Tags | Descrição |
|---|---|---|---|
omni_chf.charging.initial.count | contador | supi | Criações de sessão de cobrança |
omni_chf.charging.update.count | contador | ref | Atualizações de sessão de cobrança |
omni_chf.charging.release.count | contador | ref | Liberações de sessão de cobrança |
omni_chf.charging.creates.total | contador | result | Total de criações de sessão de cobrança |
omni_chf.charging.updates.total | contador | result | Total de atualizações de sessão de cobrança |
omni_chf.charging.releases.total | contador | result | Total de liberações de sessão de cobrança |
omni_chf.sessions.active.count | medidor | -- | Número de sessões de cobrança ativas |
Métricas do CGRateS
| Métrica | Tipo | Tags | Descrição |
|---|---|---|---|
omni_chf.cgrates.calls.count | contador | method, result | Chamadas JSON-RPC do CGRateS |
omni_chf.cgrates.latency.milliseconds | medidor | -- | Latência da chamada CGRateS |
omni_chf.cgrates.health | medidor | -- | Saúde da conectividade do CGRateS (1=ativo, 0=inativo) |
omni_chf.cgrates.requests.total | contador | operation, result | Total de solicitações JSON-RPC do CGRateS |
omni_chf.cgrates.request.duration_ms | distribuição | operation | Duração da solicitação CGRateS em ms (baldes: 5, 10, 25, 50, 100, 250, 500, 1000, 2500) |
Métricas do NRF
| Métrica | Tipo | Tags | Descrição |
|---|---|---|---|
omni_chf.nrf.registration.status | medidor | nf_type | Status de registro do NRF (1=registrado, 0=não registrado) |
Métricas da VM BEAM
| Métrica | Tipo | Descrição |
|---|---|---|
beam.memory.total | medidor | Total de memória BEAM em bytes |
beam.memory.processes | medidor | Memória usada por processos Erlang |
beam.memory.processes_used | medidor | Memória realmente usada por processos |
beam.memory.system | medidor | Memória do sistema |
beam.memory.atom | medidor | Total de memória de átomos |
beam.memory.atom_used | medidor | Memória de átomos usada |
beam.memory.binary | medidor | Memória binária |
beam.memory.code | medidor | Memória de código |
beam.memory.ets | medidor | Memória da tabela ETS |
beam.processes.count | medidor | Número de processos Erlang |
beam.ports.count | medidor | Número de portas Erlang |
beam.atom.count | medidor | Número de átomos |
beam.vm.uptime | medidor | Tempo de atividade da VM em segundos |
Padrões de Log
| Nível | Padrão | Significado |
|---|---|---|
info | CHF Create: ref=<UUID> supi=<SUPI> pdu_session=<N> | Criação bem-sucedida iniciada |
info | CHF Update: ref=<UUID> | Solicitação de atualização recebida |
info | CHF Release: ref=<UUID> | Solicitação de liberação recebida |
info | CHF CDR: %{...} | CDR emitido na liberação |
info | Initiating CGRateS session for <ref>, account: <SUPI> | CGRateS InitiateSession enviado |
info | CGRateS authorized <N> units for session <ref> | Crédito concedido |
info | CGRateS session <ref> terminated successfully | CGRateS TerminateSession OK |
warning | CGRateS integration disabled, returning default authorization | Modo de bypass ativo |
warning | CHF Update: unknown ref=<UUID> | Atualização para sessão inexistente |
warning | CHF Release: unknown ref=<UUID> | Liberação para sessão inexistente |
error | CHF Create failed: <reason> | Falha na operação de criação |
error | CHF Update failed: <reason> | Falha na operação de atualização |
error | CGRateS InitiateSession failed for <ref>: <reason> | Erro do CGRateS na criação |
error | CGRateS HTTP error <status>: <body> | Não-200 do CGRateS |
error | CGRateS HTTP request failed: <reason> | Erro de rede para o CGRateS |
Limitações Conhecidas
| ID | Severidade | Descrição |
|---|---|---|
| CHF-M1 | Médio | invocationSequenceNumber é codificado como 1 na resposta de Criação (Inicial). De acordo com a TS 32.291, o número de sequência deve começar em 1 e ser incrementado nas respostas subsequentes, o que acontece na Atualização. O problema é que, se um consumidor enviar uma Criação com um invocationSequenceNumber na solicitação, esse valor não é inspecionado ou validado. |
| CHF-M3 | Médio | invocationTimeStamp está ausente do ChargingDataResponse. De acordo com a TS 32.291, este campo é obrigatório no corpo da resposta. Consumidores rigorosos que exigem este campo receberão uma resposta incompleta. |
| CHF-M5 | Médio | O cliente CGRateS usa o cliente HTTP :httpc do Erlang em vez do Finch. :httpc não suporta pooling de conexão; cada chamada JSON-RPC abre e fecha uma conexão TCP. Sob carga (muitas sessões de cobrança simultâneas), a latência da chamada CGRateS aumentará e a sobrecarga de configuração da conexão se tornará significativa. Monitore omni_chf.cgrates.request.duration_ms. |
| CHF-L1 | Baixo | Nenhum campo triggers é incluído no ChargingDataResponse. De acordo com a TS 32.291, os gatilhos podem instruir o SMF a enviar uma atualização intermediária em eventos específicos (limite de volume, limite de tempo, mudança de QoS). Sem gatilhos, o SMF usa sua própria política local para determinar quando enviar atualizações. |
| CHF-L2 | Baixo | ratingGroup em multipleUnitInformation é codificado como 1. Implementações reais normalmente têm vários grupos de classificação por sessão PDU (um por fluxo de dados de serviço). Todo uso é atribuído ao grupo de classificação 1, independentemente dos valores de ratingGroup nos multipleUnitUsage da solicitação. |
| CHF-L3 | Baixo | Nenhum chargingId no formato 3GPP é gerado. De acordo com a TS 32.290, os registros de cobrança devem carregar um chargingId que correlacione com o ID da sessão PDU atribuído pelo SMF. O UUID charging_data_ref é usado em vez disso, o que pode causar problemas de correlação em sistemas de faturamento downstream que esperam o formato chargingId 3GPP. |
| CHF-L4 | Baixo | O registro CDR está faltando os campos chargingID e recordingEntity exigidos pela TS 32.290. Sistemas de mediação ou faturamento downstream que esperam esses campos precisarão tolerar sua ausência ou ser configurados para tratá-los como opcionais. |
| CHF-L5 | Baixo | A cobrança offline e a saída de arquivo CDR não estão implementadas. Os CDRs são emitidos apenas para o log da aplicação via Logger.info. Não há saída de CDR baseada em arquivo, nenhuma codificação ASN.1 e nenhuma transferência para um gateway de domínio de faturamento. Para cobrança offline em produção, um coletor de logs (por exemplo, Fluentd, Vector) deve coletar as linhas de log CDR do CHF e transformá-las para o sistema de faturamento. |
Solução de Problemas
404 na Atualização ou Liberação
O chargingDataRef não corresponde a nenhuma sessão ativa em memória. Causas:
- OmniCHF reiniciado entre Criação e Atualização/Liberar — todo o estado da sessão está em memória e é perdido no reinício.
- O SMF enviou o
chargingDataReferrado no caminho. - Uma Liberação foi enviada anteriormente para esta sessão, o que excluiu o contexto.
Verifique os logs para CHF Update: unknown ref= ou CHF Release: unknown ref= para confirmar.
500 na Criação com CGRateS habilitado
A chamada CGRateS falhou. Verifique:
- O
cgrates_urlestá apontando para uma instância CGRateS acessível? - O
cgrates_tenantestá correto? Nomes de inquilinos incompatíveis fazem com que o CGRateS retorne uma resposta de erro. - Verifique a métrica
omni_chf.cgrates.health(1=ativo, 0=inativo). - Revise os logs para
CGRateS InitiateSession failed for <ref>: <reason>— a razão será uma das:{:cgrates_error, message},{:http_error, status}, ou{:http_error, reason}. - Verifique se o serviço
SessionSv1do CGRateS está habilitado na configuração do CGRateS.
Verificação de saúde do CGRateS falhando, mas o daemon está em execução
A verificação de saúde (via SessionSv1.GetActiveSessions) usa um tempo limite de 3 segundos. Se o CGRateS estiver lento para responder, a verificação de saúde pode falhar enquanto o serviço está tecnicamente disponível. Verifique cgrates_timeout — o limite é min(cgrates_timeout, 3000). Também confirme se o cgrates_url usa HTTP (não HTTPS) a menos que TLS esteja configurado.
CDRs não aparecendo ou incompletos
Os CDRs são escritos no log da aplicação no nível INFO (veja a limitação CHF-L5). Para capturar CDRs:
- Certifique-se de que o nível de log da aplicação esteja definido como
infoou inferior. - Filtre as linhas de log contendo
"CHF CDR:"para processamento posterior. - Observe que os CDRs estão faltando
chargingID,recordingEntity(CHF-L4), e terãoratingGroup: 1para todos os fluxos de dados de serviço (CHF-L2).
Alta latência de chamadas do CGRateS
Como o CGRateS usa :httpc sem pooling de conexão (CHF-M5), a latência aumenta sob carga. Para diagnosticar:
- Verifique o histograma
omni_chf.cgrates.request.duration_mspara latência p99. - Se a latência for alta sob carga concorrente, reduza o número de sessões de cobrança concorrentes ou considere implantar o OmniCHF atrás de um balanceador de carga com várias instâncias.
- Como uma solução alternativa, defina
cgrates_timeoutpara um valor inferior ao tempo de resposta esperado do CGRateS para evitar que chamadas lentas do CGRateS bloqueiem o pool de processos Elixir.
Contagem de sessões ativas não diminuindo após liberações
Se omni_chf.sessions.active.count permanecer elevado após as sessões terem sido liberadas:
- Verifique as respostas 404 nas chamadas de Liberação — se o SMF receber 404, pode não tentar novamente e o SMF considera a sessão liberada enquanto o OmniCHF pode ainda ter o contexto.
- No caso inverso, se o OmniCHF reiniciou e perdeu o estado da sessão, os contextos se foram, mas o SMF pode ainda enviar solicitações de Atualização/Liberar que resultam em 404. Este é o comportamento esperado.
Registro do NRF não mantido
Verifique a métrica omni_chf.nrf.registration.status. Se estiver em 0:
- Verifique se o
nrf_uriestá correto e se o NRF é acessível a partir dosbi_addrdo OmniCHF. - Verifique se
mccemnccorrespondem à configuração PLMN do NRF. - Revise os logs da aplicação na inicialização para erros de registro do NRF.