В этой статье мы реализуем вызов между двумя телефонами с помощью Kamailio, познакомимся с SIP диалогами и какие транзакции в него входят
Теория обработки звонка (вызова) в SIP протоколе с разбором примеров в Kamailio
Для примера возьмем вызов между пользователем с номером 1111 и пользователя с номером 1112. Трассировка сигнализации будет выглядеть следующим образом:
Давайте более детально разберем, что мы видим на этой трассировке. Но перед этим несколько важных комментариев:
Условимся, что у нас есть некий SIP сервер с IP адресом 78.***.241.199 (в данном случае Kamailio), на изображении сверху. Он реализует на данный момент функции регистрации (с помощью registrar модуля) и обработку сигнализации, а также пропускает через себя RTP трафик. В дальнейшем я буду просто писать SIP сервер.
У нас есть два абонента с номерами: 1111, 1112. Назовем абонента с номером 1111 — абонент А, а абонента с номером 1112 — абонентом B. Оба эти абонента (A,B) зарегистрированы на нашем SIP сервере, как это делается мы рассматривали в предыдущей статье.
Сейчас мы разберем ситуацию, когда абонент А хочет установить сеанс связи (позвонить) абоненту B. И мы в этот момент будем снимать трассировку SIP сигнализации с сервера (с помощью утилиты sngrep). Более подробно про то, что мы видим:
- Абонент А отправляет запрос INVITE. Это запрос который указывает, что номер A хочет установить сеанс связи с номером B.
- После чего, так же как и при регистрации из предыдущей статьи, SIP сервер отправляет запрос на аутентификацию, что бы убедится, что пользователь имеет права на выполнение запроса об установлении сеанса связи. В ответ SIP сервер отправляет 407 Proxy Authentication Required.
- User Agent A(телефон, с которого был отправлен invite абонентом А) подтверждает получение запроса на авторизацию от SIP сервера. Абонент А отправляет ответ ACK в сторону SIP сервера
- После чего, телефон абонента А заново отправляет запрос на установление сеанса связи (INVITE) и добавляет в SIP сообщение заголовок Proxy-Authorization в котором указывает данные для авторизации (логин/пароль/домен(realm) в зашифрованном виде).
- Если данные из заголовка Proxy-Authorization корректные, SIP сервер отправляет ответ 100 trying (может быть указано любое сообщение вместо «trying»).
- SIP сервер проверяет, зарегистрирован ли номер B (вызываемый номер с которым хочет установить сеанс номер А) и если номер зарегистрирован, то отправляет запрос INVITE номеру Б.
- Абонент B (User Agent) получая запрос на инициализацию сеанса связи отправляет ответ 100 trying
- Абонент B (User Agent) начинает проигрывать звук вызова и отравляет вызывающему SIP серверу ответ 180 ringing
- SIP сервер транслирует гудки которые передает пользователь B пользователю А (передавая сообщение 180 ringing). При этом пользователь А начнет слышать гудки у себя в телефоне.
- Как только абонент B взял трубку, то телефон абонента B отправляет сообщение 200 OK обратно на сервер, абоненту А. Получением абонентом А ответа 200 OK означает, что абонент B взял трубку и начался разговор.
На этом заканчивается первая транзакция INVITE-200OK. После чего создается новая транзакция ACK:
- Абонент А, получив ответ 200 OK от абонента B, отправляет сообщение о подтверждении начала разговора — ACK. Это сообщение является уже новой транзакцией. Если сообщение ACK не доходит от абонента А, абоненту B в течении 30 секунд — вызов завершится. Чаще всего, обрыв соединения на 30-32 секунде означает, что где-то проблемы с трансляцией сетевых адресов (NAT). Более подробно мы рассматривали это во второй статье цикла (Прохождение NAT в SIP протоколе и Kamailio).
- После чего происходит передача голоса через SIP сервер. SDP/RTP будет рассматриваться в следующей статье.
На этом заканчивается вторая транзакция ACK. Далее, когда абоненты завершают разговор создается последняя транзакция:
- Любой из абонентов, желая завершить разговор, отправляет SIP сообщение BYE. В нашем примере, абонент B отправляет запрос на завершение разговора с абонентом A. SIP сервер транслирует это сообщение абоненту А
- Абонент А, получив запрос BYE отправляет подтверждение о получении запроса на завершение разговора — 200 OK в сторону SIP сервера. SIP сервер транслирует ответ абонента А, абоненту B.
Это последняя, финальная транзакция в приведенном примере. На этом SIP диалог завершился, SIP диалог — это совокупность всех вышеперечисленных транзакций совершенных между абонентами. Стоит упомянуть, что транзакций может быть больше, чем 3.
Я привел выше очень сжатую выдержку о том, как происходит соединение. Более подробно можно ознакомится в RFC 3261
Практика обработки вызова (SIP INVITE) в Kamailio
Как мы узнали из теоретической части нам необходимо:
- Обработать первую транзакцию INVITE-200 OK в Kamailio
- Обработать транзакцию ACK
- Обработать транзакцию завершения вызова (BYE)
Первым делом добавим необходимые модули к остальным модулям:
loadmodule "rr.so"
Модуль rr — позволяет добавлять заголовки Record-Route в SIP сообщения
Далее, внесем некоторые изменения в конфигурационный файл /etc/kamailio/kamailio.cfg из предыдущего урока. Мы удалим все функции force_rport (используется для отправки ответов на тот же порт, с которого пришел запрос, а не который указан в User Agent).
Добавим пользовательский маршрут REQINIT и добавим вызов функции force_rport() и добавим маршрут в начало главного маршрута request_route:
request_route {
route(REQINIT);
if (is_method("INVITE") || is_method("REGISTER")) {
route(NAT);
}
if (is_method("REGISTER")) {
route(AUTH);
}
}
После чего создадим сам маршрут REQINIT (в дальнейшем он так же будет использоваться для fail2ban)
route[REQINIT] {
force_rport();
}
Теперь начнем реализацию обработки SIP диалога. Добавим вызов пользовательского маршрута DIALOG в котором реализуем всю логику вызова
request_route {
route(REQINIT);
if (is_method("INVITE") || is_method("REGISTER")) {
route(NAT);
}
if (is_method("REGISTER")) {
route(AUTH);
}
route(DIALOG);
}
Обработка INVITE — 200 OK транзакции в Kamailio
Создадим ранее вызванный маршрут DIALOG:
route[DIALOG] {
if (is_method("INVITE")) {
route(AUTH);
if (!lookup("location")) {
sl_send_reply("403", "Forbidden");
exit;
}
handle_ruri_alias();
record_route();
route(RELAY);
}
}
Теперь более подробно рассмотрим, что в этом маршруте мы делаем.
Проверяем действительно ли полученное сообщение является INVITE с помощью функции is_method из модуля textops (рассматривался ранее в предыдущих статьях).
В случае, если полученное сообщение INVITE, делаем вызов пользовательского маршрута AUTH, чтобы проверить, действительно ли вызывающий абонент А является клиентом нашего SIP сервера. Сам маршрут AUTH рассматривался в предыдущей статье (Детальный разбор SIP регистрации в Kamailio). В этой статье я опишу только разницу между аутентификацией SIP сообщений REGISTER и INVITE.
При методе INVITE и вызове функции auth_check проверяется наличие SIP заголовка Proxy-Authorization. Если такого заголовка нет — выполяется тот же алгоритм, что и при REGISTER, но вместо ответа 401 Unauthorized, Kamailio отправляет запрос на авторизацию вызова с помощью 407 Proxy Authentication Required.
SIP агент абонента А, получив такое сообщение, подтверждает получение запроса на авторизацию отправляя на SIP сервер Kamailio ответ ACK. После чего телефон абонента А отправляет повторный INVITE с указанием заголовка Proxy-Authorization с логином, паролем, realm. В случае корректности данные, Kamailio отправляет ответ 100 trying.
Продолжим разбирать пользовательский маршрут DIALOG. После вызова маршрута AUTH (см. выше), если все ок — проверяем, зарегистрирован ли пользователь с помощью функции lookup из модуля registrar. Если найден AOR (Address Of Record, берется из заголовка Contact) — продолжаем выполнение маршрута DIALOG. Если же вызываемый абонент B не найден, то тогда отправляет ошибку 403 Forbidden, которая сообщает что пользователь не зарегистрирован. Если абонент зарегистрирован, вызываем функцию handle_ruri_alias(), которая заменяет Destination URI на IP адрес, который находится в alias контакта. Более подробно мы это рассматривали во второй части (Прохождение NAT в SIP протоколе и Kamailio) цикла статей.
Функция record_route() записывает маршрут и добавляет заголовок Record-Route в пересылаемое абоненту B сообщение. В данном случае функция record_route используется для Dialog Stateful — весь SIP диалог проходит через Kamailio сервер. Т.е чтобы мы могли обработать транзакцию INVITE-200 OK, транзакцию ACK и транзакцию завершения BYE через Kamailio. Добавление заголовка Record-Route в SIP сообщения, означает что любой SIP метод(запрос) будь то INVITE, BYE, 200OK, CANCEL и другие будут проходить через SIP сервер (в нашем случае Kamailio)
После чего мы создадим маршрут RELAY:
route[RELAY] {
t_on_reply("REPLY");
t_relay();
}
Функцией t_relay() мы отправляем ответ в указанный Destination URI (адрес удаленной стороны). Функция t_relay является stateful т.е она запоминает куда было отправлено SIP сообщение.
Функция t_on_reply — более интересна, т.к с помощью onreply_route (об этом ниже) мы можем обрабатывать ответы (200 ОК в нашем случае) между абонентами.
Достаточно запомнить, что в Kamailio есть несколько типов пользовательских маршрутов. На данный момент мы рассматривали только инициализирующие маршруты (INVITE, REGISTER) по типу route[NAME], сейчас мы будет использовать маршрут по типу onreply_route — служит для обработки ответов в SIP диалоге (ответы, которые относятся к 2xx, например 200 OK)
Давайте создадим маршрут для обработки ответов и назовем его «REPLY»
onreply_route[REPLY] {
route(NAT);
}
Маршрут REPLY делает только одно действие, при получении ответа (в нашем случае 200OK), маршрут вызывает другой пользовательский маршрут NAT с помощью которого узнает публичный IP адрес в поле Contact.
Давайте сделаем первый вызов между абонентом А(номер 1111) и абонентом B(1112). В моем случае я буду использовать softphone Zoiper. При вызове мы увидим следующую трассировку:
Здесь мы можем увидеть ранее описанный механизм (в начале этой статьи) об обработке первой транзакции INVITE-200 OK. Единственное на что хотелось бы обратить внимание — пересылаемое сообщение об поднятии трубки абонентом B, 200 OK. Давайте посмотрим какой ответ возвращает абонент B нашему SIP серверу Kamailio:
2021/02/14 11:38:46.170695 185.165.***.51:1114 -> 78.**.241.199:5060
SIP/2.0 200 OK
Via: SIP/2.0/UDP 78.**.241.199;branch=z9hG4bK25d5.c286e5b119550a700c1e508bd7c8ec43.0
Via: SIP/2.0/UDP 192.168.0.103:5060;rport=1162;received=185.165.***.5*;branch=z9hG4bK-524287-1---dbc86a38c52edc4e
Record-Route: <sip:78.**.241.199;lr;ftag=2858ec10>
Contact: <sip:[email protected]:51330>
To: <sip:[email protected]>;tag=e9f4a137
From: <sip:[email protected];transport=UDP>;tag=2858ec10
Call-ID: K7XbiqpmGuzEbxtLh_Pg1Q..
CSeq: 2 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.101
s=Z
c=IN IP4 192.168.0.101
t=0 0
m=audio 58170 RTP/AVP 8 0 101
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=sendrecv
Разберем более детально ответ 200 OK, который приведен выше.
1) Мы видим заголовок Record-Route с помощью которого мы делаем stateful proxy. Вызов этой функции означает, что все SIP сообщения буду проходить через Kamailio, а не напрямую между пользователями.
2) Более интересно разобрать поле Contact. Как вы видите абонент B отправляет свой приватный IP адрес нашему Kamailio серверу. При попытке отправить ответ ACK, Kamailio будет пытаться отправить ответ на IP 192.168.0.101, что совершенно неправильно. И как раз при получении ответа 200 OK, Kamailio будет обрабатывать ответ с помощью onreply_route[REPLY] (помните ранее созданный маршрут?). Вызывая маршрут NAT, соответственно для прохождения NAT’а. Kamailio добавит в поле Contact alias с публичный IP адресом абонента B. Давайте посмотрим какой ответ 200 OK, Kamailio ретранслирует абоненту А:
2021/02/14 11:38:46.177116 78.47.241.199:5060 -> 185.165.163.51:1162
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.0.103:5060;rport=1162;received=185.165.***.51;branch=z9hG4bK-524287-1---dbc86a38c52edc4e
Record-Route: <sip:78.**.241.199;lr;ftag=2858ec10>
Contact: <sip:[email protected]:51330;alias=185.165.***.51~1114~1>
To: <sip:[email protected]>;tag=e9f4a137
From: <sip:[email protected];transport=UDP>;tag=2858ec10
Call-ID: K7XbiqpmGuzEbxtLh_Pg1Q..
CSeq: 2 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: 224
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 32888 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:32889
Как видно, Kamailio узнал публичный IP адрес абонента B и добавил его в Contact как alias. После чего ретранслировал этот ответ абоненту А. На этом обработка транзакции INVITE-200OK завершена.
Обработка ACK транзакции в Kamailio
Если посмотреть ранее приведенную трассировку, можно заметить, что абонент А отвечает ACK нашему Kamailio сереру, но ничего не происходит. Это происходит из-за того, что Kamailio не умеет обрабатывать ACK транзакцию. Давайте сейчас реализуем этот функционал.
Т.к в нашем случае Kamailio является stateful proxy, который запоминает состояние SIP диалогов, мы можем воспользоваться функцией loose_route с помощью которой мы понимает, что SIP сообщение (в нашем случае ACK) проходит через Kamailio транзитом. Все return code этой функции можно посмотреть в официальной документации модуля rr. Давайте добавим обработку транзитных сообщений в Kamailio, в ранее созданный маршрут DIALOG:
route[DIALOG] {
if (is_method("INVITE")) {
route(AUTH);
if (!lookup("location")) {
sl_send_reply("403", "Forbidden");
exit;
}
handle_ruri_alias();
record_route();
route(RELAY);
}
if (has_totag()) {
if (loose_route()) {
handle_ruri_alias();
route(RELAY);
}
}
}
Добавленный код подсвечен красным. Давайте более подробно посмотрим, что мы делаем.
- Проверяем наличие тэга в поле To
- Если тэг присутствует — вызываем функцию для идентификации SIP сообщения loose_route()
- Если маршрут (вызов) найден, вызываем функцию handle_ruri_alias, которая подменяет URI удаленной стороны на поле из alias, заголовка Contact.
- После чего вызываем маршрут RELAY, который описывался выше.
После добавления обработки ACK транзакции попробуем совершить вызов:
Как видно, теперь Kamailio умеет обрабатывать транзакции INVITE-200OK, а так же новую транзакцию ACK. По трассировке мы видим, что абонента А (номер 1111) хочет совершить вызов абоненту B (номер 1112), отправляя INVITE и проходя аутентификацию, Kamailio отправляет запрос абоненту B, при принятии вызова абонентом B — отправляется ответ 200 OK абоненту А. Абонент А, получая подтверждение начала разговора (200 OK) отправяет ответ с новой транзакцией ACK. Kamailio, увидив сообщение ACK и найдя в памяти вызов, ретранслирует ответ ACK абоненту B.
Теперь давайте посмотрим, что произойдет при отправке запроса на завершение разговора BYE:
Как видно из трассировки, абонент А хочет завершить разговор. Он отправляет SIP сообщение BYE (новую транзакцию) абоненту B. Т.к мы реализовали выше обработку транзитных SIP сообщений с помощью функции loose_route, Kamailio так же корректно находит вызов и отправляет ответ абоненту B, который в свою очередь подтверждает завершение вызова ответом 200 OK. Стоит так же упомянуть, что однозначно идентифицировать сеанс связи в SIP протоколе можно с помощью заголовков From, To (тэги которые в них содержаться) и Call-ID. Далее вы поймете, почему это важно помнить.
На этом SIP диалог завершен. Но давайте посмотрим, что произойдет, если абонент А захочет отменить вызов, пока абонент B не взял трубку:
Как видно, когда абонент хочет отменить запрос на установление связи с абонентом B, он отправляет запрос CANCEL. Запрос CANCEL отменяет только запрос INVITE. Разница между методом CANCEL и BYE в том, что CANCEL отменяет еще не начавшийся сеанс связи, когда BYE заканчивает уже начавшийся сеанс связи.
Обработка CANCEL в Kamailio
Для понимания процесса завершение еще не установленного разговора, давайте посмотрим значение branch в заголовке Via. Для начала посмотрим значение branch для сообщения INVITE:
Теперь давайте посмотрим значение branch в запросе на отмену еще не начавшегося разговора, на вызов CANCEL от абонента А:
Как видно, и в запросе INVITE, и в запросе CANCEL значение поля branch совпадает. Как вы помните, установленный сеанс связи можно идентифицировать с помощью From, To tag и Call-ID. Давайте посмотрим на сообщение CANCEL:
2021/02/14 23:57:48.512204 185.165.***.51:1068 -> 78.**.241.199:5060
CANCEL sip:[email protected];transport=UDP SIP/2.0
Via: SIP/2.0/UDP 192.168.0.103:5060;branch=z9hG4bK-524287-1---4124273c551f0045
Max-Forwards: 70
To: <sip:[email protected]>
From: <sip:[email protected];transport=UDP>;tag=3e142f64
Call-ID: wXKxfBRYTNtQth1Todmu7w..
CSeq: 2 CANCEL
Proxy-Authorization: Digest username="1111",realm="ipcalls24.com",nonce="YCmsEWApquU1NWHVgZIacWItalvaUePr",uri="sip:[email protected];transport=UDP",response="cbafd83c274137a297e73e78d6c34e48",cnonce="600942b2c8ef27cab9a2a7eb0bb88671",nc=00000002,qop=auth,algorithm=MD5
User-Agent: Z 5.4.9 rv2.10.11.7
Content-Length: 0
Как видно из CANCEL, у нас есть заголовок Call-ID, а также тэг заголовка From. Но у нас нет тэга в заголовке To, т.к вызов еще не начался (от вызываемой стороны не пришел 200 OK). Поэтому вызов необходимо идентифицировать с помощью branch, который совпадает в обоих случаях между INVITE и CANCEL.
Теперь нам необходимо научить Kamailio обрабатывать запрос CANCEL. Для этого нам необходимо:
- Проверить SIP сообщение на соответствие методу CANCEL
- Проверить транзакцию, к которому относится вызов CANCEL
- Отправить сообщение CANCEL вызываемому абоненту B
Давайте реализуем это в коде. Для этого добавим в изначальный маршрут request_route следующие строки:
request_route {
route(REQINIT);
if (is_method("CANCEL")) {
if (t_check_trans()) {
route(RELAY);
}
exit;
}
if (is_method("INVITE") || is_method("REGISTER")) {
route(NAT);
}
if (is_method("REGISTER")) {
route(AUTH);
}
route(DIALOG);
}
Теперь, когда Kamailio получит любое SIP сообщение и оно начнет идти по request_route, будет всегда выполняться проверка на соответствие SIP метода. Если полученное SIP сообщение будет являться CANCEL, начнет исполнятся код. Давайте более подробно его и рассмотрим:
- Проверка на метод CANCEL, если проверка возвращает истину — начинаем выполнять следующие шаги
- Проверяем, существует ли транзакция с помощью функции t_check_trans. Если транзакция найдена идем дальше.
- Вызываем маршрут RELAY, который отправит запрос CANCEL вызываемому абоненту B. После чего выполнит выход из маршрута и остановит дальнейшую обработку по request_route
Давайте проверим, как сейчас это работает. Совершим вызов абонентом А (номер 1111) абоненту B (номер 1112), после чего, не поднимая трубку абонентом B, абонент А отправит запрос CANCEL для завершения еще не установленного сеанса связи:
Как видно из трассировки, когда абонент А отправляет запрос CANCEL, Kamailio ретранслирует (с помощью ранее добавленной обработки CANCEL) этот запрос абоненту B, который в свою очередь отвечает подтверждением 200 OK. Но давайте посмотрим далее. Абонент B отправляет SIP сообщение 487 Request Terminated, который означает, что вызов был прерван абонентом А. Kamailio получив такое сообщение, ретранслирует его абоненту А, а сам отвечает абоненту B подтверждением ACK, о том что SIP сервер получил 487 Request Terminated.
Абонент А, получив запрос 487 Request Terminated, отправляет свое подтверждение на Kamailio, но Kamailio еще не умеет обрабатывать запросы ACK, кроме тех, которые относятся к транзакции INVITE-200OK. Давайте этому научим, добавим в пользовательский маршрут DIALOG следующие строки:
route[DIALOG] {
if (is_method("INVITE")) {
route(AUTH);
if (!lookup("location")) {
sl_send_reply("403", "Forbidden");
exit;
}
handle_ruri_alias();
record_route();
route(RELAY);
}
if (has_totag()) {
if (loose_route()) {
handle_ruri_alias();
route(RELAY);
}
}
if (is_method("ACK")) {
if ( t_check_trans() ) {
route(RELAY);
exit;
} else {
exit;
}
}
}
Для корректной обработки ACK, который не относится к уже установленному сеансу связи мы добавим отдельную проверку на метод ACK.
- Проверим, является ли метод ACK
- Если возвращаемое значение истино, то выполняется проверка на существование транзакции и если такая транзакция найдена — Kamailio завершает сеанс связи между абонентом А и Kamailio (между абонентом B и kamailio сеанс был завершен ранее, когда Kamailio отправил подтверждение ACK в ответ на ошибку 487 от абонента B). После чего прекращаем обработку с помощью exit.
- Если же транзакция не найдена — ничего не делаем, прекращаем обработку этого запроса с помощью exit
Теперь давайте повторим вызов от абонента А, абоненту B и завершим вызов абонентом А до получения 200 OK (сигнализирующего о том, что сеанс связи установлен) от абонента B:
Как видно, теперь вызов завершился полностью корректно. Получив CANCEL от вызывающего абонента, Kamailio ретранслировал его вызываемому абоненту, после чего получив от вызываемого абонента ответ 487 Request Terminated — подтвердил завершение транзакции с вызываемым абонентом, отправив ответ ACK. После чего ретранслировал ответ 487 Request Terminated вызывающему абоненту и обработал ACK ответ, после чего транзакция так же закрылась.
Так же стоит упомянуть, что если абонент B не захочет принимать вызов и нажмет на сброс (отправив 486 Busy Here), Kamailio так же корректно обработает это и перешлет ответ вызывающему абоненту.
Заключение
В этой статье мы разобрали обработку SIP диалога, разобрались какие бывают транзакции и научились их обрабатывать в Kamailio. В следующей статье мы начнем работать с htable и первым делом сделаем fail2ban для того, чтобы завершить работу с SIP сигнализацией (пока), после чего настроим пропуск RTP потоков через Kamailio с помощью модуля rtpengine.
Итоговый код доступен по ссылке (kamailio.cfg)
Если есть желание сделать пожертвование, то это можно сделать кликнув по ссылке или же нажав на кнопку. Спасибо за поддержку !)
11 ответов к “Kamailio часть 3. Обработка вызова (INVITE), SIP диалог, транзакции. Подробное описание SIP INVITE с примерами”
Спасибо за превосходно поданный материал. В данной статье пропустили добавление вот этих строк перед совершением первого тестового вызова.
«`
if (is_method(«REGISTER»)) {
save(«location»);
exit;
} else {
return;
}
«`
Спасибо за обратную связь.
Не совсем понял что и где пропустил, можете подсказать строки в pastebin?
и в базе должно отразится все это
kamctl db show location
Здравствуйте.
У меня, когда клиент отправляет INVITE с авторизационными данными, kamailio вместо 100 trying отвечает 200 OK. Подскажите куда копать? Делал все по статье…
Здравствуйте. Покажите ваш kamailio.cfg (не забудьте удалить данные подключения к БД), например на pastebin/github
Максим, приветствую. Попробовал разверуть вашу схему в yandex-cloud. Получается, что клиенты сидят за своим Nat, а kamailio с частным адресом в yandex за своим. Так вот при установлении соединения абонент В отвечает 200OK, kamailio пересылает этот 200OK абоненту А но с уже добавленым Record-Route в котором приватный адрес kamailio. Соответсвенно абонент А пытается ответить ACK на этот приватный адрес из Record-Route и естественно на этом все рассыпается. Тут какие то нюансы при работе в облаке или я где то ошибся в конфиге ?
Добрый вечер. Да, есть нюансы. Как раз подробно они рассматриваются в новой статье
Максим, это просто супер ! Вам надо книгу написать, успех гарантирован ) Отправил свое скромное спасибо.
Если можно еще пару моментов по теме. Лично у меня отказался кушать строку вида listen=udp:LOCAL_IP:5060 advertise PUBLIC_IP:5060 name «public_socket», пришлось убрать name и все после, потом вместо переменной в set_send_socket_name указать реальные параметры. Но это мелочь. А вот есть вопрос по rtpengine. Если мы используем только один интерфейс с серым IP и разнесли внешнюю и внутреннюю ноги только с помощью разных sip-портов не требует ли это особой конфигурации rtpengine ? У меня сигнализация побежала как надо, а вот голос пока не победить.
Верно подметили, вылетело из головы об этом написать. Статью обновил.
Отличная статья ! Поимел конечно секса пытясь корректно запустить rtpengine на Ubuntu, но в итоге сдался и все переделал на Debian. В итоге все сложилось как надо.
От себя еще порекомендовал бы добавить в конфиг
loadmodule «debugger.so»
modparam(«debugger», «cfgtrace», 1)
modparam(«debugger», «log_level_name», «exec»)
Все таки иметь логи в реальном времени очень полезно когда что то не клеится.
https://pastebin.com/JYk6XGPz вот код если вдруг кому нужно