segunda-feira, 24 de julho de 2023

Yeti Switch - SOFTSWITCH PARA HUMANOS!!

Versão 1.12

O sistema operacional suportado é Debian GNU/Linux 11, a única arquitetura suportada é amd64.

Configuração de repositórios 

A maioria dos servidores pode usar o mesmo conjunto de repositórios:

deb http://ftp.debian.org/debian/ bullseye main contrib non-free
deb http://ftp.debian.org/debian/ bullseye-updates main
deb http://security.debian.org/debian-security bullseye-security main

deb http://pkg.yeti-switch.org/debian/bullseye 1.12 main
deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main

Os repositórios do sistema podem ser alterados editando o arquivo: /etc/apt/sources.list. Como usamos nosso próprio repositório de pacotes, você deve adicionar nossa chave a um arquivo confiável.

# apt install -y vim wget gnupg net-tools
# wget http://pkg.yeti-switch.org/key.gpg -O - | apt-key add -
A chave do repositório PGDG também deve ser adicionada à lista confiável:
# wget https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | apt-key add -

Roteando a instalação do banco de dados

NOTA: Recomendamos colocar o banco de dados de roteamento no mesmo host com interface WEB de gerenciamento para melhor desempenho e capacidade de resposta da interface web.

Versões suportadas

A única versão do PostgreSQL suportada é a 13.

Instalação de pacotes 

# apt update  
# apt install postgresql-13 postgresql-contrib-13 postgresql-13-prefix postgresql-13-pgq3  
# apt install postgresql-13-pgq-ext postgresql-13-yeti postgresql-13-pllua pgqd

Criação dos bancos de dados 

Criar banco de dados de roteamento 

# su - postgres
$ psql
postgres=# create user yeti encrypted password 'algumasenha' superuser;
CREATE ROLE
postgres=# create database yeti owner yeti;
CREATE DATABASE
postgres=# \q 
postgres@yeti:~$ exit 

NOTA: Recomenda-se escolher nomes de bancos de dados, nomes de usuários e senhas diferentes dos especificados neste manual por motivos de segurança.

Para grandes instalações, é recomendável colocar o banco de dados CDR em um servidor dedicado.

Verificações

Verifique se os bancos de dados foram criados com sucesso e estão acessíveis: 

root@yeti:/# psql -h 127.0.0.1 -U yeti -d yeti
Password for user yeti:  psql (9.4.5) SSL connection
(cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

yeti=# \q
root@yeti:/#

Não se esqueça de fazer alterações em /etc/postgresql/13/main/pg_hba.conf e aplicá-las se você planeja acessar esses bancos de dados de outros hosts e/ou configurar a replicação do banco de dados.

Instalação de bancos de dados CDR

O sistema requer dois bancos de dados: um para roteamento e outro para CDRs. A configuração de diferentes instâncias do PostgreSQL é altamente recomendada para possibilitar a replicação.

Instalação de pacotes 

Bancos de dados CDRs requerem um conjunto de pacotes semelhante ao banco de dados de roteamento.

# apt update 
# apt install postgresql-13 postgresql-contrib-13 postgresql-13-prefix postgresql-13-pgq3 
# apt install postgresql-13-pgq-ext postgresql-13-yeti postgresql-13-pllua pgqd

NOTA: É importante instalar o Postgresql do repositório PGDG. Consulte a configuração dos repositórios para obter detalhes. 

Criação dos bancos de dados

Criar banco de dados cdr (Call Detail Record).

# su - postgres
$ psql
postgres=# create user cdr encrypted password 'algumasenha' superuser;
CREATE ROLE
postgres=# create database cdr owner cdr;
CREATE DATABASE
postgres=# \q 
postgres@yeti:~$ exit

NOTA: Recomenda-se escolher nomes de bancos de dados, nomes de usuários e senhas diferentes dos especificados neste manual por motivos de segurança. 

Para grandes instalações, é recomendável colocar o banco de dados CDR em um servidor dedicado.

Código PGQd

Após a inicialização do banco de dados CDR, você deve executar pgq ticker daemon (pgqd) no servidor com banco de dados CDR. Edite o arquivo de configuração /etc/pgqd.ini e realize as modificações a seguir:

[pgqd]
base_connstr = host=127.0.0.1 port=5432 dbname=cdr user=cdr password=algumasenha
initial_database = cdr
database_list = cdr
pidfile = /var/run/postgresql/pgqd.pid

Então você deve iniciar o ticker: 

# service pgqd start
# servide pgqd status
 pgqd.service - PgQ maintenance daemon
Loaded: loaded (/lib/systemd/system/pgqd.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2023-07-24 14:07:52 -03; 47min ago
Main PID: 6949 (pgqd)
Tasks: 1 (limit: 2323)
Memory: 1.1M
CPU: 46ms
CGroup: /system.slice/pgqd.service
└─6949 /usr/bin/pgqd /etc/pgqd.ini
 

Verificações 

Verifique se os bancos de dados foram criados com sucesso e estão acessíveis:

root@yeti-cdr:/# psql -h 127.0.0.1 -U cdr -d cdr
Password for user cdr:  psql (9.4.5) SSL connection
(cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

cdr=# \q
root@yeti-cdr:/#

Não se esqueça de fazer alterações em /etc/postgresql/13/main/pg_hba.conf e aplicá-las se você planeja acessar esses bancos de dados de outros hosts e/ou configurar a replicação do banco de dados.

instalação da interface WEB

Requisitos do servidor: 

  • Arquitetura amd64;
  • Distribuição Debian 11 Bullseye;
  • Pelo menos 4 GB de RAM.

Instalação de pacotes

# apt update && apt install yeti-web nginx
 

Configuração de conexão de bancos de dados 

Para configurar os parâmetros de conexão de bancos de dados, crie o arquivo /opt/yeti-web/config/database.yml com o seguinte conteúdo: 

production:
  primary:
    adapter: postgresql
    encoding: unicode
    database: yeti
    username: yeti
    password: algumasenha
    host: 127.0.0.1
    port: 5432
    schema_search_path: 'gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import'
    min_messages: notice
    pool: 5

  cdr:
    adapter: postgresql
    encoding: unicode
    database: cdr
    username: yeti
    password: algumasenha
    host: 127.0.0.1
    port: 5432
    schema_search_path: 'cdr, reports, billing'
    min_messages: notice
    pool: 5
    migrations_paths: db/cdr_migrate
 
Você deve especificar endereços e credenciais corretos que foram usados na seção anterior.

Configuração Yeti-web

Copie o exemplo do arquivo de configuração /opt/yeti-web/config/yeti_web.yml.distr para /opt/yeti-web/config/yeti_web.yml

# cp -av /opt/yeti-web/config/yeti_web.yml.distr /opt/yeti-web/config/yeti_web.yml

Inicialização da comunicação com os bancos de dados

Para inicializar bancos de dados vazios durante a instalação inicial:

# cd /opt/yeti-web
# su -s /bin/bash yeti-web
$ RAILS_ENV=production ./bin/bundle.sh exec rake db:schema:load db:migrate
$ RAILS_ENV=production ./bin/bundle.sh exec rake db:seed
yeti-web@yeti:~$ exit
root@yeti:/opt/yeti-web# cd /

Para atualizar os bancos de dados para a versão mais recente: 

# cd /opt/yeti-web
# su -s /bin/bash yeti-web
$ RAILS_ENV=production ./bin/bundle.sh exec rake db:migrate
yeti-web@yeti:~$ exit
root@yeti:/opt/yeti-web# cd /

Configuração do Nginx

Para configuração básica, remova a configuração padrão e copie yeti-web.dist.nginx:

# rm /etc/nginx/sites-enabled/default
# cp /opt/yeti-web/config/yeti-web.dist.nginx /etc/nginx/sites-enabled/yeti
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# service nginx restart

nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-07-24 15:22:34 -03; 4s ago
       Docs: man:nginx(8)
    Process: 16821 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 16822 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 16823 (nginx)
      Tasks: 2 (limit: 2323)
     Memory: 2.6M
        CPU: 15ms
     CGroup: /system.slice/nginx.service
             ├─16823 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             └─16824 nginx: worker process

Lançar

Após a configuração bem-sucedida dos bancos de dados, você finalmente pode executar o software usando os seguintes comandos:

# service yeti-web start
# service yeti-cdr-billing@cdr_billing start
# service yeti-cdr-billing@cdr_stats start
# service yeti-delayed-job start
# service yeti-scheduler start

Isso executará a interface da WEB, as agendas de processos, processos de tarefas agendadas em segundo plano e os serviços de processamento do CDR.

Verificações 

Verifique se o puma escuta no soquete local:

# netstat -lpn | grep puma
unix 2 [ACC] STREAM OUVINDO 41901 16860/puma 6.2.1 (u /run/yeti/yeti-unicorn.sock)

verifique se o nginx escuta em endereços e portas TCP/IP corretos: 

# netstat -lpn | grep nginx
tcp 0 0 127.0.0.1:6666 0.0.0.0:* OUÇA 16823/nginx: master
tcp 0 0   0.0.0.0:80   0.0.0.0:* OUÇA 16823/nginx: master

Arquivos de log para verificar possíveis avisos/erros: 

  • /var/log/yeti/yeti-web.log
  • /var/log/yeti/yeti-cdr-billing.log
  • /opt/yeti-web/log/puma.stdout.log
  • /opt/yeti-web/log/puma.stderr.log

Tente abrir a interface de gerenciamento em seu navegador favorito e faça o login com as credenciais padrão:

User: admin

Pass: 111111

E por hoje é só, pessoal!  

Fonte:  https://yeti-switch.org/

segunda-feira, 3 de julho de 2023

Asterisk® SCF™ - PJSIP Negociação Avançada de CODEC

 


Prefácio

Este post não está completo e nem o software em 15 de julho de 2020. Ainda há muitas coisas para imprementar e/ou testar.

  • Direct Media (Mídia Direta);
  • 100rel/early media;
  • re-INVITES;
  • Fax;
  • Multi-Stream (Transmissão Múltipla);
  • Deferred SDP (Deferimento do SDP);
  • ARI Channel Operations (Operações do Canal ARI);
  • Interoperabilidade com outros drivers de canal;
  • Função do Dialplan;
  • etc.
Bem, ainda estamos trabalhando nisso.

Introdução

Com o lançamento do Asterisk® SCF™ versão 18, vem um novo processo de Negociação Avançada de CODEC. Isso não apenas cria novas oportunidades de configuração, mas também refatora completamente o próprio processo de negociação. O resultado é um processo de negociação mais fácil de entender, imprementado em muito menos código. O driver do canal PJSIP é atualmente o único a adotar o novo processo, mas seus outros drivers podem ser alterados para usá-lo no futuro. No entanto, eles teriam que adotar a interface do STREAMS.

Arquitetura

Ao pensar no processo de negociação, estamos realmente falando sobre a negociação de topologias de fluxo entre duas entidades. Para cada canal, existe uma topologia que contém um ou mais fluxos que descrevem a mídia. Na verdade, o processo de negociação acontece stream a stream, mas vamos usar a " chamada de áudio simples " como exemplo. A topologia conteria apenas um fluxo de " áudio ", mas esse fluxo poderia, é claro, permitir vários codecs.

Uma vez que o Asterisk® SCF™ é um Agente de Usuário Back-2-Back, não há praticamente nenhum cenário (mesmo com Mídia Direta) onde as partes chamadoras e chamadas negociam diretamente umas com as outras. Temos 2 canais e o CORE do Asterisk® SCF™ envolvido. Com isso em mente, se Alice chama BobAlice negocia com o driver de canal de Alice, o driver de canal de Alice negocia com o driver de canal de Bob via CORE do Asterisk® SCF™, então o driver de canal de Bob negocia com Bob. Diante disso, existem 4 pontos de controle onde podemos alterar o comportamento do processo de negociação...

  • Depois de recebermos a oferta de SDP de Alice, mas antes de o driver enviar a oferta ao CORE do Asterisk® SCF™. (incoming_offer);
  • Depois que o CORE do Asterisk® SCF™ enviou a oferta para o canal de Bob, mas antes de enviarmos uma oferta SDP para Bob. (outgoind_offer);
  • Depois de recebermos a resposta SDP de Bob, mas antes de enviá-la ao CORE do Asterisk® SCF™. (incoming_answer);
  • Após o CORE do Asterisk® SCF™ ter enviado a resposta para o canal de Alice, mas antes de enviarmos uma resposta SDP para Alice. (outgoing_answer).
Em todos esses casos, temos duas topologias que podemos alternar, uma " pendente "(pending) e uma " configurada " (configured), e várias operações de seleção e filtragem que podemos realizar nelas. O resultado é uma topologia " resolvida " (resolved). O processo de resolução é controlado por quatro parâmetros...
  • prefer: Qual da lista de CODECs no stream nós preferimos? O pendente ou o configurado? 
    • incoming_offer: Este é bastante óbvio. Pendente é a topologia criada analisando a oferta do SDP de Alice e Configurada é aquela criada a partir da lista de CODECs permitidos no terminal de Alice.
    • output_offer: Este é um tanto óbvio. Pendente é o resultado da resolução incoming_offer de Alice que foi recebida do CORE do Asterisk® SCF™ e Configurado é aquele criado a partir da lista de CODECs permitidos no terminal de Bob.
    • incoming_answer: Este é menos óbvio. Pendente é o resultado da análise da resposta SDP de Bob e  Configurado é o que enviamos a Bob na oferta.
    • outgoing_answer: Este também é menos óbvio. Pendente é o resultado da resolução incoming_answer de Bob e Configurado é o resultado da resolução incoming_offer de Alice.
Então, o que prefer realmente significa? Leia...
  • operation: Agora que sabemos qual topologia preferimos, que operação queremos realizar nelas?
    • union: Combinamos os codecs de ambas as topologias começando com a lista preferencial e adicionando ao final os codecs da lista não preferencial que ainda não estavam na lista preferencial. Basicamente, estamos preservando a ordem da topologia preferida.
    • intersect: Começamos com a lista preferida novamente, mas descartamos quaisquer CODECs que não estejam em ambas as listas. Isso mantém apenas os CODECs comuns enquanto preserva a ordem da lista preferencial.
    • only_preferred: Apenas usamos a lista de preferidos e descartamos completamente a lista de não preferidos.
    • only_nonpreferred: Apenas usamos a lista de não preferidos e descartamos completamente a lista de preferidos.
  • keep: Agora que temos uma lista filtrada e ordenada, o que guardamos dela?
    • all: Mantenha-os todos;
    • first: Passe apenas pelo primeiro CODEC na lista resultante.
  • Transcode: Finalmente, permitimos a transcodificação ou não?
    • allow: Permite que a chamada prossiga mesmo se a lista resultante não tiver CODECs nela.
    • preventNÃO permite que a chamada prossiga se a lista resultante não tiver CODECs nela.
Configuração

Os quatros pontos de controle e seus parâmetros são todos configurados nos terminais PJSIP. Os parâmetros do ponto de controle são nomeados...
  • codec_prefs_incoming_offer;
  • codec_prefs_outgoing_offer;
  • codec_prefs_incoming_answer;
  • codec_prefs_outgoing_answer.
Os parâmetros são especificados como pares <nome>:<valor> separados por vírgulas (o espaço em branco é ignorado). Aqui está um exemplo, incluindo uma declaração de permissão, mostrando os padrões para cada ponto de controle...


Padrões de preferência de negociação de CODEC

allow = !all,g722,ulaw
codec_prefs_incoming_offer = prefer: pending, operation: union, keep: all, transcode: allow
codec_prefs_outgoing_offer = prefer: pending, operation: intersect, keep: all, transcode: allow
codec_prefs_outgoing_offer = prefer: pending, operation: union, keep: all, transcode: allow
codec_prefs_outgoing_offer = prefer: pending, operation: union, keep: all, transcode: allow


Você notará que os padrões sempre preferem a topologia "pendente", portanto, em nosso exemplo, o que Alice envia em sua oferta SDP define o cenário.

Realmente Simples

Vamos começar com uma chamada básica em que Alice e Bob são configurados com suas configurações padrão e ambos com um parâmetro "allow" definido como "!all,ulaw,g722".

  • Alice faz uma oferta => Asterisk® SCF™ => Bob:
    • Alice envia um INVITE com uma oferta SDP contendo ulaw e g722 nessa ordem.
    • O canal de Alice resolve essa topologia com os codecs configurados de seu endpoint e as preferências de incoming_offer. Como preferimos Pendentes e a operação é union (união), o resultado do primeiro estágio não surpreende: ulaw,g722. Vamos manter ambos os CODECs e a transcodificação realmente não importa neste momento.
    • O canal de Alice então envia a chamada para o CORE do Asterisk® SCF™ junto com a topologia resolvida.
    • O CORE do Asterisk® SCF™ invoca o dialplan que cria o canal de saída de Bob e encaminha a topologia resolvida para ele.
    • O canal de Bob agora resolve a topologia que veio do CORE do Asterisk® SCF™ (pendente) com os CODECs configurados de seu próprio terminal e as preferências de output_offer. A única diferença entre suas preferências e as de Alice é a operação. Como ambas as topologias já são iguais, o resultado ainda é ulaw,g722.
    • Com base nisso, o canal de Bob cria um INVITE de saída com uma oferta SDP contendo ulaw,g722 nessa ordem.
  • Bob aceita a oferta => Asterisk® SCF™ => Alice.
    • Bob responde a oferta com uma resposta do tipo SDP contendo apenas ulaw.
    • O canal de Bob resolve a topologia de resposta recebida (pendente) com a topologia enviada para Bob (configurada) que tem ulaw,g722 e as preferências de resposta recebida de Bob. Como a operação é union (união), a topologia resolvida contém apenas ulaw.
    • O driver de canal de Bob passa a topologia resolvida de volta ao CORE do Asterisk® SCF™, pois indica que a chamada está sendo atendida (answered).
    • O CORE do Asterisk® SCF™ passa a topologia resolvida de volta para o canal de Alice, uma vez que informa que Bob respondeu (answered).
    • O canal de Alice resolve a topologia do CORE do Asterisk® SCF™ (pendente), com a topologia que originalmente enviou para o CORE do Asterisk® SCF™ (configurado) que tinha ulaw,g722 e as preferências de outgoing_answer de Alice. Novamente, como a operação é union (união), a topologia resolvida contém apenas ulaw.
    • O canal de Alice então envia a resposta SDP de volta para Alice com apenas ulaw.
Nesta chamada, Alice deu o tom para a chamada.

Uma mudança na ordem

Sabemos que os telefones de Alice e Bob podem suportar g722, mas seus telefones sempre listam ulaw primeiro. Então, como podemos fazê-los usar o g722? Vamos fazer algumas alterações de configuração...
  • Começamos alterando as preferências de oferta de entrada de Alice para preferir a topologia configurada em vez da pendente. 
    • codec_prefs_incoming_offer = prefer: configured, operation: union, keep: all, transcode: allow
  • Em seguida, configuramos os CODECs de Alice para g722,ulaw.
    • allow = !all,g722,ulaw
 
Agora vamos ligar...

  • Alice faz uma oferta => Asterisk® SCF™ => Bob:
    • Alice envia um INVITE com uma oferta SDP contendo ulaw e g722 nessa ordem.
    • O canal de Alice resolve essa topologia (pendente) com os CODECs configurados de seu terminal (g722,ulaw) e as preferências de incoming_offer. Como preferimos configurado e a operação é union (união), o resultado do primeiro estágio agora é g722,ulaw.

O restante da chamada flui como antes, exceto que g722,ulaw é a topologia pendente. Pode haver um " gotcha " (te peguei). A RFC3264 afirma que um agente de usuário que recebe uma oferta NÃO DEVE alterar a ordem dos codecs ao criar sua resposta. Não diz que NÃO DEVE alterar o pedido. Portanto, é possível, embora altamente improvável, que Bob possa responder a g722,ulaw com ulaw,g722. Se for esse o caso, você pode forçar Bob a usar g722 definindo seu parâmetro de manutenção de outgoing_offer como primeiro. Desta forma, enviaremos apenas g722. Claro, se ele não suporta g722, você não deveria tê-lo configurado em seu terminal em primeiro lugar. 😂

Transcodificação

Isso é onde fica complicado...
  • Digamos que o terminal de Alice esteja configurado apenas com alaw como CODEC, mas ela envia apenas ulaw,g722. Se a topologia resolvida estiver vazia, como seria se a operação fosse union (união), a chamada é encerrada imediatamente com um 488 Not Acceptable Here. Não importa qual é o parâmetro de transcodificação definido porque, neste ponto, nem sabemos qual é o canal de saída.
  • Agora vamos supor que o endpoint de Alice tenha ulaw,g722. Como o terminal dela também tinha ulaw,g722, nós o enviamos para o CORE do Asterisk® SCF™. O endpoint de Bob, no entanto, tinha apenas um CODEC, alaw! E sua operação era union (união). Isso resultaria em uma topologia resolvida vazia. Para que a transcodificação seja considerada, o parâmetro de transcodificação de incoming_offer de Alice e o parâmetro de transcodificação de outgoing_offer de Bob DEVEM ser configurados para permitir. Se um deles for "impedir", a chamada será encerrada. Se ambos forem permitidos, enviaremos uma oferta a Bob com alaw como CODEC.
  • Se Bob responder sem CODECs, a chamada será encerrada. Novamente, porém, você provavelmente não deveria ter configurado o endpoint de Bob com um CODEC não suportado. Caso contrário, a topologia resolvida (alaw) é passada de volta para o CORE do Asterisk® SCF™.
  • O canal de Alice obtém a topologia do núcleo (pendente) e a resolve em relação ao que foi enviado ao CORE do Asterisk® SCF™ (configurado) e às preferências de outgoing_answer de seu terminal. Se a topologia resultante estiver vazia, como seria neste caso, o parâmetro de transcodificação output_answer será verificado. Se for permitido, o canal usará a topologia originalmente enviada ao CORE do Asterisk® SCF™ para construir a resposta de saída e simplesmente ignorará a topologia resolvida. Se o parâmetro transcode for prevent (o que provavelmente foi uma configuração incorreta), a chamada será encerrada.

Implementação nos bastidores

A implementação antiga tinha negociação de CODEC espalhada por chan_pjsipres_pjsip_session e res_pjsip_sdp_rtp. A ACN tenta consolidar toda a negociação de CODECs em chan_pjsip, mas ainda há resquícios nos outros módulos que precisarão ser refatorados. Um bom exemplo é a função "set_caps" em res_pjsip_sdp_rtp. Ele é chamado tanto nas respostas recebidas quanto nas respostas enviadas, mas na verdade não queremos que ele seja executado para respostas enviadas, pois ele tenta definir os limites para o que estava na oferta recebida original. Isso não é bom. Tudo funciona como pretendido, mas é um código inútil. Outro problema é que muitas das funções nos módulos res_pjsip são reutilizadas e não têm ideia do contexto em que são executadas. Por exemplo, apply_negotiated_sdp é executado para respostas recebidas e enviadas (é assim que set_caps é executado duas vezes). De qualquer forma...

  • Alice faz uma oferta => Asterisk® SCF™ => Bob:
    • incoming_offer: Isso é tratado em chan_pjsip:chan_pjsip_incoming_request( ) antes que o canal seja realmente criado. Essa função é chamada por sessão por meio do suplemento handle_incoming_request. A função resolve a topologia criada a partir da oferta de Alice com os parâmetros e CODECs do endpoint e do endpoint de Alice. Supondo que haja fluxos ativos deixados na topologia resolvida, a função redefine a topologia pendente na sessão para ser a topologia resolvida e chama chan_pjsip_new, que define a topologia no canal junto com os limites de formato nativo e os formatos de leitura e gravação. NOTA: Não definimos os CODECs de instância RTP aqui, mas deveríamos. Se a resolução falhar, terminamos com um 488.
    • Eventualmente, trabalhamos até app_dial:dial_exec_full, que cria o canal de saída de Bob com ast_request_with_stream_topology( ) passando na topologia do canal de Alice.
    • outgoing_offerast_request_with_stream_topology( ) chama chan_pjsip_request_with_stream_topology( ). Em seguida, isso resolve a topologia de solicitação (pendente) com os parâmetros output_offer do endpoint de Bob e os CODECs de endpoint do endpoint de Bob. Supondo que existam fluxos ativos deixados na topologia, a função chama chan_pjsip_new( ) que define a topologia do canal de Bob, limites de formato nativo e formatos de leitura/gravação. Mesma observação acima, devemos definir os CODECs de instância RTP aqui, mas ainda não. Se a topologia resolvida, incluindo a aplicação de opções de transcodificação, não tiver mais fluxos ativos, retornamos uma causa de AST_CAUSE_FAILURE para app_dial e bail, o que faz com que um 503 seja enviado para Alice.
    • dial_exec_full finalmente chama chan_pjsip_call( ) cuja tarefa call( ) chama ast_sip_session_create_invite( ) então ast_sip_session_send)request( ).

  • Bob aceita a oferta => Asterisk® SCF™ => Alice.
    • incoming_answerQuando Bob envia um 200OKpjproject chama nosso callback session_inv_on_media_update( ) que então chama res_pjsip_session:handle_negotiated_sdp( ). Isso define a topologia ativa para aquela recebida de Bob. Eventualmente, pjproject sinaliza uma mudança de estado de transação com base no recebimento de 200OK, que aciona os suplementos handle_incoming_response da sessão, um dos quais é chan_pjsip_incoming_response_after_media( ). Isso resolve a topologia ativa que veio de Bob, com a topologia que enviamos a Bob usando as preferências de resposta recebida do endpoint de Bob. A função então chama ast_queue_control_data com um tipo de quadro AST_CONTROL_ANSWER e a topologia resolvida como os dados.
    • app_dial:wait_for_answer( ) recebe o quadro ANSWER e coloca a topologia na estrutura de configuração da ponte. Isso é passado para features:ast_bridge_call( ) e para baixo para pre_bridge_setup( ) que chama ast_raw_answer_with_stream_topology( ). Isso, por sua vez, chama chan_pjsip_answer_with_stream_topology no canal de Alice.
    • output_answer: a tarefa de resposta de chan_pjsip_answer_with_stream_topology faz a resolução final usando a topologia ativa de Bob, a topologia pendente de Alice que foi originalmente enviada para o CORE do Asterisk® SCF™ e os parâmetros de output_answer do terminal de Alice.

Asterisk® SCF™ - Utilizando PJSIP como Proxy

 

Existe muitos cenários de proxy diferentes nos quais o Asterisk® SCF™ pode estar envolvido. Nem todos podem ser explicados aqui, mas alguns exemplos podem ajudá-lo a se adaptar à sua situação específica. O primeiro e mais simples cenário é onde o Asterisk® SCF™ está funcionando como um PBX na mesma rede privada em que os telefones estão, mas precisa de conectividade com um Provedor de Serviços de Telefonia pela Internet (ITSP - Internet Telephony Service Provider).

Outbound Proxy (Proxy de Saída)

Assumiremos que o ITSP exige que o Asterisk® SCF™ se registre para receber chamadas. Claro, mesmo com o Asterisk® SCF™ atrás de um firewall ou roteador NAT, um proxy não é realmente necessário, mas a configuração é boa para começar. Embora a configuração de um proxy como o Kamailio® esteja além do escopo deste post, este cenário requer apenas as configurações de proxy mais simples e provavelmente funcionaria com os exemplos fornecidors pelo Kamailio®. Vamos assumir que o proxy é DUAL HOMED com uma interface na rede privada e uma inteface na rede pública. Também assumiremos que o proxy está retransmitindo mídia e sinalização. Usaremos 192.168.15.1 como endereço privado do proxy e 192.168.15.2 como endereço do Asterisk® SCF™ (atuando como um Softswitch PBX IP).

Configuração do Asterisk® SCF™

Existem vários objetos PJSIP que precisam ser configurados para esta situação.

  • transport: Na verdade, esta é uma ação de desconfiguração 😂. Se o Asterisk® SCF™ não estiver usando um proxy, você pode ter parâmetros no transporte como external_signalling_addressexternal_media_addresslocal_net, etc. Estes NÃO deve ser configurados quando o Asterisk® SCF™ e o PROXY estiverm na mesma rede. O Asterisk® SCF™ não deve saber nada sobre o que está do outro lado do proxy, pois o trabalho do proxy é tornar isso invisível.
Examplo:
[ipv4-udp]
type = transport
protocol = udp
bind = 0.0.0.0:5060
  • endpoint: Configure o endpoint do ITSP como faria normalmente, mas adicione um parâmetro outbound_proxy com um URI que aponta para o endereço interno do proxy. Isso direcionará (quase) todas as solicitações de saída desse endpoint para o proxy. Você também não deve ativar nenhum parâmetro reladionado ao NAT, como force_rportice_support, etc.
Examplo:
[myitsp]
type = aor
contact = sip:my.itsp.com:5060
outbound_proxy = sip:192.168.0.1\;lr
qualify_frequency = 60
  • registration: O mesmo que aor. Os URIs do clinte e do servidor devem permanecer como estavam para a situação sem proxy e o parâmetro outbound_proxy deve ser incluído.
Examplo:
[myitsp]
type = registration
client_uri = sip:my_account@my.itsp.com
server_uri = sip:my.itsp.com
outbound_proxy = sip:192.168.0.1\;lr
  • identify: agora as coisas ficam um pouco complicadas. A mairoria dos ITSPs não autentica de volta para seus clientes ao enviar as chamadas e eles podem estar enviando o CallerID do originador/chamador como o usuário do cabeçalho FROM: (DE), portanto, a (quase) única maneira de identificar as chamdas da ITSP é pelo endereço IP. Se não houvesse proxy no circuito, você provavelmente configuraria um objeto de identificação com um parãmetro match = my.itsp.com. No caso do proxy, porém, a correspondência precisa ser com o endereço privado do proxy, pois esse é o endereço IP de onde os pacotes virão.
          Examplo:
[myitsp]
type = identify 
match = 192.168.0.1
endpoint = myitsp

Você deve ter notado que os URIs do proxy têm o parâmetro " lr " adicionado. Isso ocorre porque a maioria dos proxies hoje em dia segue o RFC 3261 e, portanto, tem " loose-routing " (roteamento flexível). Se você não o tiver definido, provavelmente receberá uma reposta 404 do proxy. O objeto " \ " antes do ponto e vírgula é importante para evitar que o ponto e vírgula seja tratado como um caractere inicial de comentário no arquivo de configuração.

Bom, nesse ponto, você deve ter um tronco com sua ITSP funcionando para chamadas de Inbound (entradas) e Outbound (saídas). 

Direct Media

Se o seu proxy for compatível, você poderá ativar a mídia direta entre os telefones e o proxy definindo direct_media = yes no telefone (device) e nos terminais (endpoint) da ITSP. O proxy deve cuiadar  do resto. Tentar fazer mídia direta, diretamente entre os telefones (devices) e o terminais (endpoints) da ITSP provavelmente não funcionará.

Multiplas ITSPs

Há um pequeno problema com a configuração acima se você tiver mais de 1 tronco ITSP por meio do proxy. Na configuração acima, o objeto de identificação é usado para direcionar solicitações recebidas do proxy para um único endpoint e você não pode direcionar o mesmo endereço IP para vários endpoints por motivos óbvios. Você pode definir 1 endpoint e 1 identificador para o proxy atuar como o receptor de chamadas de todos os provedores de serviços, mas isso nem sempre é conveniente ou possível com algumas GUIs front-end.

Nesse caso, e se o seu ITSP suportar, você pode usar o parâmetro de linha do objeto de registro como o mecanismo para corresponder as solicitações recebidas a um terminal e eliminar completamente o uso de identificar. 

Veja como funciona: Quando você especifica line = true e endpoint = <endpoint> em um registro, o Asterisk® SCF™ acrescenta um parâmetro " line " ao URI de contato do REGISTER de saída que contém uma string exclusiva. Ficará assim: " Contact: <sip:s@192.168.0.2.245:5060;line=eylpkkv> ". Se a sua ITSP suportar, quando enviar uma solicitação INVITE para o Asterisk® SCF™, ele incluirá o parâmetro " line " no Request URI ou no cabeçalho To da seguinte forma: " INVITE sip:8005551212@192.168.0.2:5060;line=eylpkkv SIP/2.0 " . O Asterisk® SCF™ então usará essa string única para corresponder a solicitação ao endpoint especificado no registro.

Examplo:
[myitsp]
type = registration
client_uri = sip:my_account@my.itsp.com
server_uri = sip:my.itsp.com
outbound_proxy = sip:192.168.0.1
line = yes
endpoint = myitsp 

Header Matching (Correspondência de Cabeçalho)

Algumas ITSPs incluem cabeçalhos " X- " em suas solicitações que contêm números de contas ou outras informações de identificação. O Asterisk® SCF™, em suas versões 13.15 e 14.5 têm um novo recurso de identificação que permite combinar as solicitações recebidas com os endpoints por meio desses cabeçalhos.

Examplo: 
[myitsp]
type = identify
match_header = X-My-Account-Number: 12345678
endpoint = myitsp 

Inbound Proxy (Proxy de Entrada)

Em um cenário de provedor de serviços (ITSP), o Asterisk® SCF™ provavelmente estará atrás de um proxy separado da internet pública e dos clientes, sejam eles telefones ou PBXes ou o que quer que seja. Nesse caso, a carga de configuração muda do Asterisk® SCF™ para o proxy. Você provavelmente desejará configurar o proxy para lidar com autenticação, qualificação, direção de mídia para gateways de mídia, servidores de correio de voz etc., e tudo isso está além do escopo deste documento. Contribuições que contenham instruções para proxies populares serão muito bem-vindas.

Fonte: https://wiki.asterisk.org/

Por George Joseph.