Рубрики
Kamailio

Kamailio часть 4. Обработка RTP потоков через sipwise RTPengine, взаимодействие SIP протокола с RTP

В этой статье, с помощью Kamailio, мы научимся обрабатывать RTP потоки с через RTPengine, разберем теория обработки RTP потоков в SIP протоколе и реализуем теорию на практике в Kamailio.

Теория взаимодействия RTP потоков в SIP протоколе с разбором примеров

SIP протокол стоит на двух слонах, первый из которых — сигнализация, которая рассматривалась в предыдущей статьях (обработка регистрации в локальной сети, обработка регистрации за NAT, обработка вызовов) и передача голоса через RTP. Если коротко, то сигнализация — это согласование вызова между двумя абонентами (и/или абонентом и SIP серверов), в этот момент пользователи не разговаривают между собой, между ними нет установленной связи и пользователи не слышат голос друг друга. Второй слон — RTP, это сам голос который слышат пользователи между собой. Обычно на вызов создается два RTP потока: от абонента А к абоненту B и наоборот, от абонента B к абоненту А. Давайте изобразим SIP диалог (SIP INVITE в Kamailio, обработка вызова, транзакция с примерами):

sip dialog in kamailio
Типичный SIP диалог между вызывающим абонентом А и вызываемым абонентом B

Красным цветом выделены интересующие нас RTP потоки. Давайте разберемся в принципе, что такое RTP.

Кодеки

Итак, мы поняли, что договариваются о самом звонке с помощью SIP сигнализации. Но что происходит после согласования вызова? Пользователи должны слышать друг друга и адекватно воспринимать сказанные слова. И в этот момент появляются кодеки. Что такое кодек (codec)?

Кодек (codec) — это способ кодирования/декодирования входящего и/или исходящего сигнала определенным способом. Кодек может быть реализован как аппаратно, так и программно.

Проще говоря, кодек — это набор правил, с помощью которых происходит преобразование голосового потока в цифровой вид, например когда абонент А говорит в микрофон мобильного телефона, в этот момент все, что говорит абонент А будет преобразовано в цифровой вид, который можно будет восстановить обратно в голос. И декодирование (представление) из цифрового вида в человечески понятный вид, т.е цифровой поток, который приходит на телефон Абонента B от Абонента А, с помощью телефона (и с помощью аппаратного или программного декодирования) преобразуется обратно в голос и абонент B таким образом слышит и понимает, что говорит абонент А.

Неполный список используемых кодеков:

  • G.711A
  • G711U
  • G722
  • Opus
  • G726

RTP протокол в SIP

Сам протокол RTP работает поверх UDP датаграмм или TCP пакетов. Ввиду того, что де-факто UDP является стандартом передачи голоса, далее мы будем рассматривать вариант RTP поверх UDP. В отдельных случаях, когда применение UDP невозможно (например WebRTC) — будут делаться отдельные примечания.

Теперь все становится немного понятней. Т.е мы через SIP сигнализацию договорились о звонке и далее передаем голос с помощью кодека через протокол RTP между двумя пользователями и все счастливы. Но возникает вопрос, что алгоритм кодирования/декодирования(кодек) не единственный. Есть множество кодеков, которые отличаются между собой, например необходимой пропускной способностью канала и способом кодирования сигнала. Для «согласования» кодеков используется протокол SDP (Session Description Protocol), в котором описывается как и куда (между какими IP адресами) должен идти голос.

Протокол SDP в SIP

Как я писал выше, существует множество кодеков и при звонке пользователи должны между собой договорится через какой кодек они будут передавать голос в цифровом виде. Для этого служит протокол SDP, который описывает сам процесс предложения/подтверждения кодека. Давайте возьмем как пример все так же вызов между Абонентом А (вызывающая сторона) и Абонентом B (вызываемая сторона):

sdp in sip invite, in kamailio

Это стандартный SIP INVITE, который мы очень подробно разбирали в предыдущей статье (SIP INVITE в Kamailio с детальным разбором примеров). Но теперь давайте обратим внимание на самый низ SIP сообщения. Красным цветом как раз и выделен содержимое протокола SDP, давайте более детально разберем каждую строку

v=0 - это версия протокола SDP, всегда указан 0 т.к это единственная доступная год версия
o=Z 1614673176855 1 IN IP4 192.168.0.103 - в этой строке указан владелец (owner) сессии. Цифры 161467... - идентификатор сессии, 1 - номер версии для этого сеанса, IN - тип сети, в данном случае Internet, IP4 - версия протокола IP (IP4 для IPv4 или IP6 для IPv6), 192.168.0.103 - IP адрес инициатора сессии
s=Z - название сессии
c=IN IP4 192.168.0.103 - в этой строке содержится информация о соединение, это необязательное поле (при наличии медиа(m=*) полей), остальные параметры такие же как и в информации о владельце (o=)
t=0 0 - первый 0 - это start timer, второй 0 - это stop timer. Если в start timer указан 0, то сеанс считается постоянным, если stop timer указан 0 - сеанс связи не ограничен по времени
m=audio 8000 RTP/AVP 3 101 8 0 - медиа настройки, audio - тип медиа, 8000 - порт для передачи данных, RTP/AVP - используемый протокол, [3,101,8,0] - это и есть цифровые обозначения используемых кодеков (полезной нагрузки). Так например, цифра 3 означает кодек GSM, цифра 101 используется для передачи DTFM сигналов, цифра 8 - кодек PCMA, цифра 0 - кодек PCMU
a=rtpmap:101 telephone-event/8000 - rtpmap служит для расшифровки из цифрового значения, а также содержит информацию о тактовой частоте кодирования. В данном случае протокол 101 используется для обработки DTMF. В этой статье DTMF рассматриваться не будет
a=fmtp:101 0-16 - сообщение о том, что устройство может работать с 0-16 кодами DTMF. Где 0-9 - это цифры, 10-16 это буквы (A,B..)
a=sendrecv - указание режима отправки и приема

Более подробно про каждый параметр и все остальные возможные параметры как обычно можно прочитать в RFC4566

RTP потоки через SIP сервер

В предыдущем разделе мы рассматривали идеальную ситуацию, когда между Абонентом А и Абонентом B нет никаких посредников и у обоих абонентов «публичный» IP адрес. Теперь давайте рассмотрим пример, когда и Абонент А, и Абонент B зарегистрированы на одном сервере и этот же сервер пропускает через себя RTP (например, для записи разговоров). Изобразим это схемой:

rtp through sip server

На схеме изображен посредник в лице SIP сервера, через который проходит вся сигнализация и весь голосовой трафик.

Примечание

softswitch 4 класса — сервер, который выполняет только функцию транзита SIP сигнализации/RTP. Т.е 4 класс подразумевает под собой только функцию маршрутизации вызовов между SIP серверами
softswitch 5 класса — сервер, который помимо маршрутизации может работать непосредственно с абонентами (обработка вызовов и регистраций) и предоставление различных дополнительных функций (IVR, очереди, голосовая почта и.т.д)

Давайте вернемся к ранее приведенному примеру запроса на установление сеанса связи (INVITE) между Абонентом А и Абонентом B

sip invite sdp

В данном случае нас интересуют IP адреса, которые мы видим в SDP (обведено красным).

И давайте так же рассмотрим ответ 200 ОК от Абонента А к Абоненту B, в котором так же есть SDP

200 ok sdp

Здесь отчетливо видно, что используется и в сообщении INVITE, и в ответе 200 OK приватные IP адреса. Что это значит? Это значит, что абонент А и Абонент B будут отправлять RTP пакеты на приватные IP адреса и соответственно друг друга абоненты слышать не будут. Для решения этой проблемы необходимо настроить RTP прокси которое, как понятно из названия, будет транслировать через себя голосовые потоки между абонентами. Давайте посмотрим на следующую трассировку:

rtp proxy

И рассмотрим несколько SIP сообщений из этой трассировке. В первую очередь обратим внимание на запрос INVITE от Абонента А к SIP серверу (первый INVITE, который находится слева), рассматривать будет только SDP поля:

v=0
o=Z 1615104545925 1 IN IP4 192.168.0.103
s=Z
c=IN IP4 192.168.0.103
t=0 0
m=audio 8000 RTP/AVP 3 101 8 0
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

Этот пакет мы уже разбирали очень подробно ранее (SIP SDP в Kamailio). Как мы уже определили, проблема здесь в «приватных» IP адресах

Примечание

В некоторых SIP агентах реализована функция при которой сам агент через STUN (или любые другие методы прохождения NAT) указывает свой публичный IP адрес для SDP сессии. В данном руководстве, мы предполагаем, что пользовательские телефоны/софтфоны понятия не имею о самом NAT и все необходимые манипуляции проводятся на SIP сервере.

Теперь же давайте посмотрим это же SIP сообщение, но уже от SIP сервера к Абоненту B (на трассировке это второй INVITE по счету)

v=0
o=Z 1615104545925 1 IN IP4 78.**.241.199
s=Z
c=IN IP4 78.**.241.199
t=0 0
m=audio 32952 RTP/AVP 3 101 8 0
a=rtpmap:3 GSM/8000
a=rtpmap:101 telephone-event/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=fmtp:101 0-16
a=sendrecv
a=rtcp:32953

Как видим, IP адреса изменились с приватного на публичный IP SIP сервера. Т.е этим сообщением, мы говорим Абоненту B, что RTP трафик следует отправлять на наш SIP серверу

Теперь же давайте посмотрим ответ 200 OK от Абонента B к SIP серверу:

v=0
o=Z 0 1 IN IP4 10.9.70.28
s=Z
c=IN IP4 10.9.70.28
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

Здесь мы видим такую же ситуацию, как и в запросе INVITE от Абонента А к SIP серверу. Но давайте так же посмотрим, как SIP сервер перешлет это сообщение Абоненту А:

v=0
o=Z 0 1 IN IP4 78.**.241.199
s=Z
c=IN IP4 78.**.241.199
t=0 0
m=audio 32968 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:32969

И мы снова видим, что изменился приватный IP адрес Абонента B на публичный IP адрес SIP сервера. Т.е мы сообщаем абоненту А, что RTP трафик следует отправлять на SIP сервер. И в этом случае, когда Абонент А и Абонент B отправляют голосовые потоки(RTP) на SIP сервер и при условии, что SIP сервер умеет проксировать через себя RTP, мы получим двухсторонюю слышимость. Это отчетливо видно на трассировке:

rtp in sip protocol

Обработка RTP потоков в Kamailio

Теперь давайте реализуем теоретическую часть на практике. Что для этого нужно?

  1. Изменить IP адрес в SIP сообщении INVITE от абонента А к Kamailio (SIP серверу) на публичный IP адрес Kamailio
  2. Изменить IP адрес в SIP ответе 200 OK от абонента B к SIP серверу на публичный IP адрес Kamailio
  3. Научить Kamailio обрабатывать через себя RTP трафик

Для реализации мы будем использовать модуль rtpengine. Этот модуль, как следует из названия, работает с RTP прокси от sipwise. Страница проекта на github доступна по ссылке. Сама установка возможно двумя вариантами. Первый — установка из уже собранного пакета для DEB совместимых систем. Второй же вариант — установка на любой дистрибутив с помощью установки из исходников. Т.к я использую CentOS 7, то я производит установку из исходников. Дублировать официальную инструкцию я не вижу смысла, поэтому условимся, что вы самостоятельно установили пакет/установили из исходников RTPEngine.

В моей установке RTPEngine доступен на порту 2223. Приведу свой пример systemd демона для запуска RTPEngine:

[Unit]
Description= RTPEngine proxy for RTP and other media streams
After=network.target iptables.service redis.service syslog.target
# iptables.service is required only if the RTPEngine uses its kernel module. redis.service is required if the Redis server is working on the same machine along with the  """"RTPEngine

[Service]
Type=simple
PIDFile=/var/run/rtpengine/rtpengine.pid
ExecStart"""" =/usr/bin/rtpengine/rtpengine-start /etc/default/rtpengine-conf
# After systemd send kill signal to the daemon, clean the iptables stuffs (delete the forwarding table, remove the kernel module + delete iptables rules)
ExecStopPost=/usr/bin/rtpengine/rtpengine-stop-post
[Install]
WantedBy=multi-user.target

И сам конфигурационный файл rtpengine-conf:

# Comment the variables that you don't want to be used for the configuration

RUN_RTPENGINE=yes
LISTEN_NG=127.0.0.1:2223
LISTEN_CLI=60001
INTERFACES="internal/172.19.0.2 external/78.**.241.199"
TIMEOUT=60
SILENT_TIMEOUT=3600
FORK=no
TABLE=40
NO_FALLBACK=yes
PORT_MIN=10000
PORT_MAX=40000

LISTEN_NG — указываю IP адрес по котором доступен RTPEngine (в данном случае только для локальной машины) и порт, в данном случае 2223.

INTERFACES — указываю приватный и внешний IP адрес. Внешний IP адрес будет использоваться для замены абонентских IP адресов в SIP сообщениях (INVITE и 200 OK)

Инициализация RTPEngine в Kamailio

Для этого в конфигурационный файл kamailio.cfg, ко всем остальным модулям, необходимо добавить инициализацию модуля rtpengine.

loadmodule "rtpengine.so"

После чего, необходимо указать адрес и порт подключения к RTPEngine с помощью modparam (добавить к остальным параметрам модулей):

modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")

На этом инциализация RTPEngine в Kamailio завершена.

Обработка SDP в Kamailio

Теперь нам необходимо реализовать теоретическую часть. Для этого в ранее созданный (Транзакция INVITE — 200 OK в Kamailio) маршрут RELAY необходимо добавить следующие строки:

route[RELAY] {
        if (has_body("application/sdp")) {
                rtpengine_manage("replace-session-connection replace-origin ICE=remove direction=internal direction=external");
        }
        t_on_reply("REPLY");
        t_relay();
}

Первым делом мы проверяем, содержит ли SIP сообщение SDP протокол. Если содержит — мы с помощью функции rtpengine_manage выполняем:

  1. replace-session-connection — заменяем в SDP сообщении поле c= на публичный IP адрес Kamailio.
  2. replace-origin — заменяем в SDP сообщении поле o= на публичный IP адрес Kamailio.
  3. ICE=remove — Удаляем из SDP сообщения все ICE атрибуты
  4. direction=internal, direction=external — добавляем поддержку нескольких сетевых интерфейсов с публичным и приватным IP адресов (понадобится в будущем при работе с media серверами)

Теперь давайте посмотрим на трассировку:

rtpengine in kamailio

Как видно из трассировки, сейчас RTP идет только в одну сторону (от Абонента B к Абоненту А через Kamailio). Почему так происходит? Давайте посмотрим два INVITE. Первым рассмотрим INVITE от Абонента A к Kamailio (рассматривать будем только SDP):

v=0
o=Z 1615107611908 1 IN IP4 192.168.0.103
s=Z
c=IN IP4 192.168.0.103
t=0 0
m=audio 8000 RTP/AVP 3 101 8 0
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

Здесь все тоже самое. Приватные IP адреса от Абонента А к Kamailio. Теперь давайте посмотрим пересылаемый INVITE (SDP) от Kamailio к Абоненту B:

v=0
o=Z 1615107611908 1 IN IP4 78.**.241.199
s=Z
c=IN IP4 78.**.241.199
t=0 0
m=audio 32978 RTP/AVP 3 101 8 0
a=rtpmap:3 GSM/8000
a=rtpmap:101 telephone-event/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=fmtp:101 0-16
a=sendrecv
a=rtcp:32979

И здесь мы уже видим, что все хорошо. Kamailio изменил приватный IP адрес в SDP на свой публичный с помощью маршрута RELAY и функции rtpengine_manage. Но почему по трассировке мы видим, что RTP идет только в одну сторону? Как вы помните, с помощью SDP мы договариваемся о кодеках и о IP адресах, куда следует отправлять RTP. В данном случае, мы добавили обработку только для запросов INVITE (т.е запроса на установление сеанса связи), поэтому Абонент B, когда увидел в SDP сообщении публичные IP адреса Kamailio и стал отправлять голос (RTP) на указанный IP адрес.

Но давайте посмотрим на SDP в сообщении 200 OK (сигнализация о том, что абонент B поднял трубку и начался разговор). Рассмотрим сначала сообщение 200 OK от Абонента B к Kamailio:

v=0
o=Z 0 1 IN IP4 10.9.70.28
s=Z
c=IN IP4 10.9.70.28
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

Здесь так же ничего необычного мы не видим, указан приватный IP адрес Абонента B. Но давайте посмотрим на SDP пересылаемое Kamailio Абоненту А:

v=0
o=Z 0 1 IN IP4 10.9.70.28
s=Z
c=IN IP4 10.9.70.28
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

И здесь мы видим, что ничего фактически не изменилось. Kamailio переслал ответ 200 OK в неизменном виде. Почему так произошло? Дело в том, что в маршруте RELAY мы обрабатываем только инициализирующие запросы (по типу INVITE). Для обработки ответов (200 OK) необходимо использовать созданный в предыдущем (SIP транзакция INVITE — 200 OK в Kamailio) маршрут onreply_route[REPLY]. Давайте добавим в этот маршрут такую же обработку SDP сообщений, как и для маршрута RELAY:

onreply_route[REPLY] {
        route(NAT);
        if (has_body("application/sdp")) {
                rtpengine_manage("replace-session-connection replace-origin ICE=remove direction=internal direction=external");
        }
}

Параметры идентичны маршруту RELAY, поэтому разбирать мы их не будем. Давайте посмотрим на трассировку теперь:

rtpengine in kamailio

Как видно из трассировки, сейчас RTP идет между Абонентом А к Абоненту B через Kamailio и наоборот. Давайте посмотрим теперь на сообщение 200 OK от Абонента B к Kamailio:

v=0
o=Z 0 1 IN IP4 10.9.70.28
s=Z
c=IN IP4 10.9.70.28
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv

Здесь все без изменений. Но давайте также посмотрим пересылаемый Kamailio ответ 200 OK абоненту А:

v=0
o=Z 0 1 IN IP4 78.47.241.199
s=Z
c=IN IP4 78.47.241.199
t=0 0
m=audio 33022 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:33023

И здесь мы уже видим, что 200 ОК полученное абонентом А содержит в себе публичный IP адрес Kamailio. Это значит, что теперь абонент А будет отправлять голос(RTP) на Kamailio так же как и абонент B. Теперь у нас есть двухстороняя слышимость.

Заключение

В этой статье мы познакомились с модулей RTPEngine, рассмотрели теорию SDP и передачи голоса в IP сетях. На практике реализовали RTP проксирование через Kamailio.

В следующей статье мы реализуем хранение регистраций абонентов в базе данных (чтобы при перезагрузке Kamailio, все регистрации не терялись), а также реализуем простой fail2ban с помощью htable.

Если есть желание сделать пожертвование, то это можно сделать кликнув по ссылке или же нажав на кнопку. Спасибо за поддержку !)

Для зарубежных платежных систем/карт

10 ответов к “Kamailio часть 4. Обработка RTP потоков через sipwise RTPengine, взаимодействие SIP протокола с RTP”

Добрый день!

А как SIP сервер узнает публичные адреса абонента А и Б чтобы отправлять им траффик?

Добрый день!
Спасибо за материал.
Могу ли я купить у вас консультацию по Kamailio?

RTPEngine отказывается запускаться на Ubuntu версии 20 и выше, есть ли возможность дописать гайд с использованием rtpproxy?

Относительно недавно rtpengine стал поставляться без компиляции в виде пакета. Т.к статья писал до этого, то советую воспользоваться https://dfx.at/rtpengine/ (официальный репозиторий rtpengine).

Добрый день. есть ли возможность добавить список команд для установки rtpengine на Centos 7?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *