fix(freeswitch): 3 achados reais de NAT entre ramais no mesmo escritório — registro, áudio e chamada morrendo em 32s (PHASE 68)
Usuário reportou que 1502 ligando pra 1503 (dois telefones IP reais, ambos registrados) não completava a chamada. Três bugs em camadas diferentes, cada um só confirmado lendo o log/capturando pacote real — nunca por suposição: 1. Registro: os dois ramais registravam com Contact apontando pro MESMO IP público compartilhado desta rede (a VM nunca tem IP público próprio, é sempre RFC1918 atrás do NAT do escritório) — originar uma chamada tentava mandar o INVITE de volta pra esse IP público, hairpin NAT clássico, falha instantânea (503). Fix: NDLB-received-in-nat-reg- contact (Contact salvo vira o IP realmente observado no pacote). De quebra, aplicado network_mode: host no serviço freeswitch (pedido explícito do usuário) — tira o Docker NAT/bridge do meio. Quebra em cascata corrigida: fs-config vira alcançável só via 127.0.0.1:8080 (não mais nome de serviço), workers ESL (fs-events/predictive-dialer) passam a usar host.docker.internal. 2. Áudio: com o registro corrigido, a chamada completava mas sem RTP — local-network-acl="localnet.auto" só cobria a subnet da própria interface do FreeSWITCH, tratando ramais de OUTRAS subnets do mesmo escritório como "de fora" e trocando o SDP pelo IP público de novo. Fix: ACL própria (b2bcall_lan, cobre todo RFC1918) referenciada em local-network-acl. 3. Chamada morrendo sozinha em exatos 32s (Timer H do RFC 3261): mesmo com áudio ok, o 200 OK que o FreeSWITCH manda pro ramal que recebeu a chamada ainda tinha Contact com o IP público — o telefone nunca manda o ACK de volta, FreeSWITCH retransmite sozinho até desistir. Só confirmado com tcpdump (instalado nesta sessão) capturando o pacote byte a byte. Fix: ext-rtp-ip/ext-sip-ip (STUN, sempre resolvem pro IP público) removidos do profile "internal" — sem endereço "externo" configurado, o FreeSWITCH nunca mais tem como escolher errado. Confirmado resolvido pelo usuário com chamadas reais nos dois sentidos, áudio bidirecional, sobrevivendo bem além dos 32s que travavam antes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
68
TODO.md
68
TODO.md
@@ -2456,6 +2456,74 @@ rota de entrada")
|
||||
os dois persistiram com o tipo certo, sobrevivendo a um reload
|
||||
completo da página.
|
||||
|
||||
## PHASE 68 — Ramal registrado mas chamada não completava / sem áudio
|
||||
(reportado pelo usuário testando com 2 telefones IP reais, 1502 e 1503)
|
||||
- [x] **Achado 1 (chamada não completava, 503 instantâneo)**: os dois
|
||||
ramais registravam com o Contact apontando pro MESMO IP público
|
||||
compartilhado desta rede (esta VM nunca tem IP público próprio —
|
||||
é sempre RFC1918 atrás do NAT do escritório). Ao originar uma
|
||||
chamada, o FreeSWITCH tentava mandar o INVITE de volta pra esse
|
||||
IP público — hairpin NAT clássico, o roteador do escritório não
|
||||
sabe refletir esse tráfego de volta pra dentro. Corrigido com
|
||||
`NDLB-received-in-nat-reg-contact` (Dockerfile) — o Contact salvo
|
||||
passa a ser o IP/porta REALMENTE observados no pacote (a rede
|
||||
interna), não o que o telefone auto-relata.
|
||||
- [x] `network_mode: host` no serviço `freeswitch` (pedido do usuário,
|
||||
"sempre é problema NAT do Docker") — tira a camada de NAT/bridge
|
||||
do Docker do meio. Quebra em cascata corrigida: FreeSWITCH sai da
|
||||
rede bridge do compose, então (a) não resolve mais os OUTROS
|
||||
containers pelo nome de serviço — `fs-config` virou
|
||||
`http://127.0.0.1:8080/` (porta publicada só em loopback,
|
||||
xml_curl.conf.xml) —, e (b) os workers que chamavam ESL via nome
|
||||
`freeswitch` (fs-events/fs-config/predictive-dialer) passam a usar
|
||||
`host.docker.internal` (+ `extra_hosts: host-gateway`, Docker
|
||||
20.10+). Segurança do Event Socket deixa de depender de publicar
|
||||
só em loopback (não existe mais essa camada) e passa a depender
|
||||
inteiramente da ACL `b2bcall_internal` já configurada
|
||||
(`apply-inbound-acl`) — confirmado que continua rejeitando
|
||||
qualquer origem fora de loopback/rede do Docker.
|
||||
- [x] **Achado 2 (chamada completava, mas sem áudio)**: com o registro
|
||||
corrigido, a ligação passou a completar, mas sem RTP (confirmado
|
||||
pelo usuário com `sngrep`). `local-network-acl="localnet.auto"`
|
||||
(padrão vanilla) só cobre a subnet da PRÓPRIA interface do
|
||||
FreeSWITCH — os ramais reais ficam em OUTRAS subnets do mesmo
|
||||
escritório, então eram tratados como "de fora" e o SDP trocava o
|
||||
RTP/SIP anunciado pelo IP público compartilhado de novo (mesmo
|
||||
hairpin, agora na mídia). Corrigido com uma ACL própria
|
||||
(`b2bcall_lan`, cobre todo RFC1918 — nunca alcançável de verdade
|
||||
"de fora" nesta VM) referenciada em `local-network-acl`.
|
||||
- [x] **Achado 3 (chamada completava e tinha áudio, mas morria sozinha
|
||||
em exatos 32s)**: mesmo com áudio já corrigido, toda chamada
|
||||
morria sozinha depois de exatos 32 segundos — o Timer H do
|
||||
RFC 3261 (64×T1, prazo padrão de qualquer UAS esperando o ACK de
|
||||
um 2xx). Só descoberto de verdade com captura de pacote real
|
||||
(`tcpdump`, instalado nesta sessão — não estava no host): o
|
||||
`200 OK` que o FreeSWITCH manda pro ramal que RECEBEU a chamada
|
||||
ainda tinha `Contact: sip:...@191.240.175.55:5060` (o IP público
|
||||
de novo, MESMO com o SDP já mostrando o IP certo) — o telefone
|
||||
nunca manda o ACK de volta (não tem como chegar por ali), o
|
||||
FreeSWITCH retransmite o 200 OK sozinho (0.5s/1s/2s/4s/4s...) até
|
||||
desistir e derrubar a chamada. `apply-nat-acl`/`local-network-acl`
|
||||
(achados 1/2) não bastam aqui — são checagens independentes, e
|
||||
trocar `apply-nat-acl` pra escopo `b2bcall_lan` (testado) não
|
||||
mudou nada nesse Contact. O fix de verdade: `ext-rtp-ip`/
|
||||
`ext-sip-ip` (STUN, resolvem pro IP público) REMOVIDOS do profile
|
||||
`internal` (Dockerfile) — sem endereço "externo" nenhum
|
||||
configurado, o FreeSWITCH não tem mais como escolher errado,
|
||||
cai sempre em `rtp-ip`/`sip-ip` (10.10.32.138). Único profile
|
||||
afetado; `external` (reservado pra um trunk PSTN real, nunca
|
||||
usado ainda) mantém os dois params intactos.
|
||||
- [x] Diagnosticado ao vivo lendo `/var/log/freeswitch/freeswitch.log`
|
||||
em tempo real durante as tentativas de chamada do usuário (nunca
|
||||
supondo causa sem ver o log) e, pro achado 3, uma captura de
|
||||
pacote real via `tcpdump` — cada achado confirmado por evidência
|
||||
exata (`NORMAL_TEMPORARY_FAILURE`/503 instantâneo pro achado 1;
|
||||
endereço do `c=`/RTP-IP pro achado 2; cabeçalho `Contact:` do
|
||||
`200 OK` capturado byte a byte pro achado 3). Confirmado resolvido
|
||||
pelo usuário com chamadas reais nos dois sentidos (1502→1503 e
|
||||
1503→1502), áudio bidirecional e chamada sobrevivendo bem além
|
||||
dos 32s que travavam toda tentativa antes do achado 3.
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user