В этой статье мы исправим некоторые недочеты седьмой части (Интеграция Kamailio и FreeSWITCH в VoIP). Мы настроим корректное прохождение RTP потоков через Kamailio + RTPengine из внешней сети в приватную для A-leg и из внутренней (приватной) сети во внешнюю сеть для B-leg. В качестве media сервера все также будет выступать FusionPBX который основан на FreeSWITCH
Теория прохождение RTP потоков через Kamailio и RTPengine
В принципе существование этой статьи я рассматриваю как более глубокое понимание работы rtpengine и как с этим работать в Kamailio. Давайте вспомним схему работы, которая сейчас реализуется, я это изображу схематично следующим образом. Для SIP A-leg (А ноги):
User UA -> Kamailio Public Interface -> Kamailio Private Interface -> FreeSWITCH Private Interface
Т.е пользователь инициирует звонок через свой User Agent (UA), будь то физический телефон зарегистрированный на балансировщике, будь то софтфон. При отправке запроса INVITE на балансировщик (Kamailio) через интернет, запрос попадает на внешний (публичный) IP адрес балансировщика (Kamailio Public Interface), после чего вызов обрабатывается согласно конфигурационному файлу и отправляется на media сервер через приватную сеть (Kamailio Private Interface) на media сервер который доступен только внутри приватной сети (FreeSWITCH Private Interface)
Для SIP B-leg (B ноги):
FreeSWITCH Private Interface -> Kamailio Private Interface -> Kamailio Public Interface -> User UA
При получении вызова от балансировщика, FreeSWITCH инициализирует второй вызов (B-leg) вызываемому абоненту. Как он это делает? Отправляя запрос через свой сетевой интерфейс (FreeSWITCH Private Interface) т.к media сервера доступны только внутри приватной сети, далее Kamailio получает на свой приватный сетевой интерфейс запрос на отправку INVITE вызываемому абоненту (Kamailio Private Interface), а т.к вызываемый абонент находится во внешней сети, происходит отправка запроса через публичный сетевой интерфейс (Kamailio Public Interface). Вызываемый абонент, получив запрос INVITE, отвечает и проходит путь в обратном направлении (через публичные/приватные) сетевые интерфейсы нашей инфраструктуры.
Цель rtpengine пропускать через себя RTP потоки, как в публичную сеть, так и в приватную. Т.е мы должны подменить IP адреса в SDP 4 раза:
1) При запросе от пользователя
2) При ответе 200 OK пользователя
3) При отправке запроса INVITE вызываемому абоненту
4) При ответе 200 OK от вызываемого абонента
И в этой статье мы разберемся, как с этим жить и исправим неправильную работу в текущем виде. Недочет предыдущей статьи заключается в том, что если мы посмотрим трассировку (из предыдущей статьи), то увидим следующее:
Давайте обратим внимание на RTP потоки. Скажу сразу, что это рабочая схема, но при этом она не до конца корректна. RTP потоки должны проходить сначала на публичный интерфейс балансировщика, после чего отправляется в приватную сеть через другой сетевой интерфейс. Обратный путь должен быть таким же, т.е сначала приватная сеть, после чего rtpengine проксирует вызовы в публичную сеть
Практика корректной обработки RTP потоков в связке Kamailio + rtpengine + FusionPBX (FreeSWITCH)
Итак, нам необходимо чтобы наш media сервер (в данном случае это FreeSWITCH) отправлял запросы/rtp потоки на внутренний (приватный) сетевой интерфейс балансировщика, а не на внешний. Для этого мы откроем FusionPBX и поправим наш dialplan для локальных вызовов (Взаимодействие Kamailio и FreeSWITCH) следующим образом:
После чего, нам необходимо поправить конфигурационный файл kamailio.cfg следующим образом.
Изменим маршрут route[RELAY]:
route[RELAY] {
if (has_body("application/sdp") && isflagset(tswitch)) {
rtpengine_manage("replace-origin external internal");
} else {
rtpengine_manage("replace-origin internal external");
}
t_on_reply("REPLY");
if (isflagset(fswitch)) {
t_relay();
}
if (isflagset(tswitch)) {
if (is_method("INVITE")) {
ds_select_dst("1", "4");
}
t_relay();
}
}
Т.е мы по разному обрабатываем SDP когда вызов отправляется в сторону media серверов (флаги tswitch & fswitch более подробно разбиралось в 7 части) и когда вызов приходит с media серверов.
О директивах internal, external можно более подробно прочитать в 4 части (Обработка RTP в Kamailio), где мы только настраивали rtpengine. А также более подробно можно прочитать в официальной документации модуля.
Если коротко, с помощью директив internal, external мы устанавливаем, какие IP адреса (внутренние или внешние) будут заменены в SDP. Из конфига выше становится понятно, что при отправке вызова с балансировщика на медиа сервер (флаг tswitch, т.е to switch) мы подменяем IP адреса с внешних IP на внутренние. При получении ответов/вызовов с медиа серверов (флаг fswitch, т.е from switch) — подменяются IP адреса с внутренних на внешние
И изменим маршрут onreply_route[REPLY]:
onreply_route[REPLY] {
route(NAT);
if (has_body("application/sdp")) {
rtpengine_manage("replace-origin");
}
}
Здесь все просто, сам модуль достаточно умный и поэтому дублировать логику не имеет смысла, т.к сам модуль разберется где нужно подменить IP адреса в SDP на внутренние/внешние.
Предлагаю посмотреть, как это работает. Совершаем звонок с номера 1111 на номер 1112. А-leg (А нога вызова):
Как видно, трассировка стала намного красивей. Теперь у нас есть явное разделение публичной (внешней) сети и приватной (внутренней сети). Давайте посмотрим на запрос INVITE который мы получаем от пользователя 1111 в сторону балансировщика и как он видоизменяется при отправке в сторону media сервера:
2021/08/29 06:05:53.941729 ***.***.***.106:23192 -> 78.**.***.***:5060
INVITE sip:[email protected];transport=UDP SIP/2.0
Via: SIP/2.0/UDP 192.168.0.100:63804;branch=z9hG4bK-524287-1---6bac5763279a1dd1
Max-Forwards: 70
Contact: <sip:[email protected]:63804;transport=UDP>
To: <sip:[email protected]>
From: <sip:[email protected];transport=UDP>;tag=482ce534
Call-ID: zlzSac1X04y61BMGbLzACA..
CSeq: 1 INVITE
Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE
Content-Type: application/sdp
User-Agent: Z 5.5.3 v2.10.15.0
Allow-Events: presence, kpml, talk
Content-Length: 177
v=0
o=Z 1630209953593 1 IN IP4 192.168.0.100
s=Z
c=IN IP4 192.168.0.100
t=0 0
m=audio 8000 RTP/AVP 8 101 0
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
Интересующие строки выделены жирный шрифтом и цветом. Теперь давайте посмотрим, как видоизменяется запрос INVITE при отправке в сторону медиа сервера:
2021/08/29 06:05:54.005154 10.0.0.3:5060 -> 10.0.0.3:5090
INVITE sip:[email protected];transport=UDP SIP/2.0
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=482ce534>
Record-Route: <sip:78.**.**.***;r2=on;lr;ftag=482ce534>
Via: SIP/2.0/UDP 10.0.0.3;branch=z9hG4bK85ab.43ada0feb2bb2e3f98c166df2cc18f63.0
Via: SIP/2.0/UDP 192.168.0.100:63804;rport=23192;received=**.***.***.106;branch=z9hG4bK-524287-1---b429ebeb1b8c40bf
Max-Forwards: 70
Contact: <sip:[email protected]:63804;transport=UDP;alias=**.***.***.106~23192~1>
To: <sip:[email protected]>
From: <sip:[email protected];transport=UDP>;tag=482ce534
Call-ID: zlzSac1X04y61BMGbLzACA..
CSeq: 2 INVITE
Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE
Content-Type: application/sdp
User-Agent: Z 5.5.3 v2.10.15.0
Allow-Events: presence, kpml, talk
Content-Length: 226
v=0
o=Z 1630209953593 1 IN IP4 10.0.0.3
s=Z
c=IN IP4 10.0.0.3
t=0 0
m=audio 10718 RTP/AVP 8 101 0
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:10719
И давайте посмотрим ответ 200 OK от медиа сервера на балансировщик:
2021/08/29 06:06:00.719776 10.0.0.3:5090 -> 10.0.0.3:5060
SIP/2.0 200 OK
Via: SIP/2.0/UDP 10.0.0.3;branch=z9hG4bK85ab.43ada0feb2bb2e3f98c166df2cc18f63.0
Via: SIP/2.0/UDP 192.168.0.100:63804;rport=23192;received=***.***.***.106;branch=z9hG4bK-524287-1---b429ebeb1b8c40bf
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=482ce534>
Record-Route: <sip:**.**.**.**9;r2=on;lr;ftag=482ce534>
From: <sip:[email protected];transport=UDP>;tag=482ce534
To: <sip:[email protected]>;tag=vHQSFyB9X0vDF
Call-ID: zlzSac1X04y61BMGbLzACA..
CSeq: 2 INVITE
Contact: <sip:[email protected]:5090;transport=udp>
User-Agent: FreeSWITCH
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY
Supported: timer, path, replaces
Allow-Events: talk, hold, conference, refer
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 269
Remote-Party-ID: "Outbound Call" <sip:[email protected]>;party=calling;privacy=off;screen=no
v=0
o=FreeSWITCH 1630191056 1630191057 IN IP4 10.0.0.3
s=FreeSWITCH
c=IN IP4 10.0.0.3
t=0 0
m=audio 18904 RTP/AVP 8 101
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off - - - -
a=ptime:20
a=rtcp:18905 IN IP4 10.0.0.3
И посмотрим, как изменится этот ответ при отправке в сторону пользователя, который инициировал вызов:
2021/08/29 06:06:00.722889 **.**.**.**9:5060 -> ***.***.***.106:23192
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.0.100:63804;rport=23192;received=***.***.***.106;branch=z9hG4bK-524287-1---b429ebeb1b8c40bf
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=482ce534>
Record-Route: <sip:**.**.**.239;r2=on;lr;ftag=482ce534>
From: <sip:[email protected];transport=UDP>;tag=482ce534
To: <sip:[email protected]>;tag=vHQSFyB9X0vDF
Call-ID: zlzSac1X04y61BMGbLzACA..
CSeq: 2 INVITE
Contact: <sip:[email protected]:5090;transport=udp>
User-Agent: FreeSWITCH
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY
Supported: timer, path, replaces
Allow-Events: talk, hold, conference, refer
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 273
Remote-Party-ID: "Outbound Call" <sip:[email protected]>;party=calling;privacy=off;screen=no
v=0
o=FreeSWITCH 1630191056 1630191057 IN IP4 **.**.*33.239
s=FreeSWITCH
c=IN IP4 **.**.*33.239
t=0 0
m=audio 10614 RTP/AVP 8 101
a=silenceSupp:off - - - -
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
a=rtcp:10615
a=ptime:20
Как видно, все работает отлично. Как вы помните, ранее мы добавили раздельную обработку при отправке в сторону медиа серверов и при получении от них запросов/ответов. В данном случае, т.к вызов приходит из внешнего мира, на запрос INVITE устанавливается флаг tswitch и в маршруте RELAY с помощью директив external, internal мы определяем, что необходимо в SDP подменить IP адреса на приватную сеть. При получении 200 OK — мы обрабатываем его через маршрут onreply_route[REPLY]
Теперь давайте посмотрим на B-leg (Б ногу вызова):
Как видно, при отправке вызове с media сервера, все также выглядит хорошо. Давайте более подробно разберем запросы/ответы в которых мы модифицируем SDP
Запрос INVITE от media сервера в сторону балансировщика:
2021/08/29 06:05:54.026219 10.0.0.3:5090 -> 10.0.0.3:5060
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP 10.0.0.3:5090;rport;branch=z9hG4bKvQD2gN9va8aXp
Max-Forwards: 69
From: "1111" <sip:[email protected]>;tag=XtgjHSvcU9j0a
To: <sip:[email protected]>
Call-ID: 3f10385f-8321-123a-848e-960000d47f0d
CSeq: 40551185 INVITE
Contact: <sip:[email protected]:5090>
User-Agent: FreeSWITCH
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY
Supported: timer, path, replaces
Allow-Events: talk, hold, conference, refer
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 263
X-FS-Support: update_display,send_info
Remote-Party-ID: "1111" <sip:[email protected]>;party=calling;screen=yes;privacy=off
v=0
o=FreeSWITCH 1630183800 1630183801 IN IP4 10.0.0.3
s=FreeSWITCH
c=IN IP4 10.0.0.3
t=0 0
m=audio 26154 RTP/AVP 8 0 101
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off - - - -
a=ptime:20
И сразу же посмотрим, как балансировщик видоизменяет его при отправке в сторону вызываемого номера (пользователя):
2021/08/29 06:05:54.029154 **.**.**.*39:5060 -> ***.***.***.106:21526
INVITE sip:[email protected]:51330;rinstance=b0c650dee5fdc3b4;transport=UDP SIP/2.0
Record-Route: <sip:**.**.**.239;r2=on;lr;ftag=XtgjHSvcU9j0a>
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=XtgjHSvcU9j0a>
Via: SIP/2.0/UDP **.**.**.239;branch=z9hG4bK9bd4.e81a230670aa518f021664617a86f949.0
Via: SIP/2.0/UDP 10.0.0.3:5090;received=10.0.0.3;rport=5090;branch=z9hG4bKvQD2gN9va8aXp
Max-Forwards: 69
From: "1111" <sip:[email protected]>;tag=XtgjHSvcU9j0a
To: <sip:[email protected]>
Call-ID: 3f10385f-8321-123a-848e-960000d47f0d
CSeq: 40551185 INVITE
Contact: <sip:[email protected]:5090>
User-Agent: FreeSWITCH
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY
Supported: timer, path, replaces
Allow-Events: talk, hold, conference, refer
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 297
X-FS-Support: update_display,send_info
Remote-Party-ID: "1111" <sip:[email protected]>;party=calling;screen=yes;privacy=off
v=0
o=FreeSWITCH 1630183800 1630183801 IN IP4 7*.**.**.239
s=FreeSWITCH
c=IN IP4 7*.**.**.239
t=0 0
m=audio 10628 RTP/AVP 8 0 101
a=silenceSupp:off - - - -
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
a=rtcp:10629
a=ptime:20
В данном случае устанавливается флаг fswitch и соответствено, мы меняем IP адреса на внешние
В ответе 200 OK от вызываемого пользователя, мы меняем адреса уже в обратную сторону, т.к вызываемый абонент отвечает нам из внешней сети. Давайте посмотрим, что отправляет пользователь в сообщении 200 OK:
2021/08/29 06:06:00.703279 ***.***.***.106:21526 -> 7*.***.***.239:5060
SIP/2.0 200 OK
Via: SIP/2.0/UDP 7*.***.**.239;branch=z9hG4bK9bd4.e81a230670aa518f021664617a86f949.0
Via: SIP/2.0/UDP 10.0.0.3:5090;received=10.0.0.3;rport=5090;branch=z9hG4bKvQD2gN9va8aXp
Record-Route: <sip:7*.**.***.239;r2=on;lr;ftag=XtgjHSvcU9j0a>
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=XtgjHSvcU9j0a>
Contact: <sip:[email protected]:51330>
To: <sip:[email protected]>;tag=8f17557f
From: "1111" <sip:[email protected]>;tag=XtgjHSvcU9j0a
Call-ID: 3f10385f-8321-123a-848e-960000d47f0d
CSeq: 40551185 INVITE
Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE
Content-Type: application/sdp
User-Agent: Zoiper rv2.10.11.7
Allow-Events: presence, kpml, talk
Content-Length: 166
v=0
o=Z 0 1 IN IP4 192.168.0.102
s=Z
c=IN IP4 192.168.0.102
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
И сразу же посмотрим, как модифицируется это же сообщение при отправке уже на медиа сервера:
2021/08/29 06:06:00.704373 10.0.0.3:5060 -> 10.0.0.3:5090
SIP/2.0 200 OK
Via: SIP/2.0/UDP 10.0.0.3:5090;received=10.0.0.3;rport=5090;branch=z9hG4bKvQD2gN9va8aXp
Record-Route: <sip:7*.***.***.239;r2=on;lr;ftag=XtgjHSvcU9j0a>
Record-Route: <sip:10.0.0.3;r2=on;lr;ftag=XtgjHSvcU9j0a>
Contact: <sip:[email protected]:51330;alias=***.***.***.106~21526~1>
To: <sip:[email protected]>;tag=8f17557f
From: "1111" <sip:[email protected]>;tag=XtgjHSvcU9j0a
Call-ID: 3f10385f-8321-123a-848e-960000d47f0d
CSeq: 40551185 INVITE
Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE
Content-Type: application/sdp
User-Agent: Zoiper rv2.10.11.7
Allow-Events: presence, kpml, talk
Content-Length: 226
v=0
o=Z 0 1 IN IP4 10.0.0.3
s=Z
c=IN IP4 10.0.0.3
t=0 0
m=audio 10728 RTP/AVP 8 0 101
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
a=rtcp:10729
a=ptime:20
Как видим, все отлично работает и у нас получается корректная трассировка как на A-leg, так и на B-leg.
Заключение
На этом я считаю, что статья заканчивается. В этой статье мы поправили предыдущие недочеты и сделали корректную инфраструктуру для проксирование RTP потоков, где явно выделили приватную/публичные сети и добавили корректную их обработку.
Если есть желание сделать пожертвование, то это можно сделать кликнув по ссылке или же нажав на кнопку. Спасибо за поддержку !)
7 ответов к “Kamailio часть 9. FreeSWITCH (FusionPBX) в роли media (backend) сервера с примерами”
Повторил все Ваши шаги на anazone ec2 сервере с одним интерфейсом. Все получилось с advertise .
Было бы здорово если бы Вы продолжили и описали настройку DMQ модуля. Готов присоединится к написанию и тестированию
Спасибо за обратную связь и предложение помочь!)
Перед новым годом большая загрузка на работе, планирую продолжить написание статей после нового года.
Лучший материал по kamailio на русском.
Отличная подача материала и объяснения.
Логичный ход конфигурации. Мне как новичку удалось всё понять и запустить kamailio+rtpengine по вашему гайду.
Пожалуйста, продолжайте писать. Очень интересует WebRTC и проброс регистрации с распределением по серверам (еще не искал как это делать, может быть всё просто).
В любом случае — спасибо!
Автору огромное спасибо за проделанную работу, отличная подача материала!
Жду продолжение цикла, очень интересует WebRTC а также использование серверов asterisk как MediaServer.
Если автор оставит линк на донаты — с радостью поддержу 🙂
Благодарю за отзыв.
Ссылку на донат добавил в статью 🙂
Привет ✋Максим как можно с тобой связаться?
Привет, можно по телеграму @maxqwe11