В этой статье мы сделаем интеграцию Kamailio (выступает в роли frontend) с FreeSWITCH (выступает в роли backend)
Как вы помните из нулевой части (Архитектура VoIP с исподьзованием Kamailio), в качестве backend (media) серверов будет использоваться FreeSWITCH. Интеграцию Kamailio + Asterisk в этой статье мы рассматривать не будем, это отдельная тема. В качестве надстройки над FreeSWITCH мы будем использовать FusionPBX, который позволяет хранить все данные (dialplan, IVR, группы и.т.д) непосредственно в базе данных. Сам FreeSWITCH использует синтаксис XML для написания конфигурационных файлов (dialplan, IVR…), FusionPBX хранит все данные в базе данных, использует lua скрипты для реализации XML Handler. Т.е переводит данные из базы данных в понятный FreeSWITCH’у вид, в XML файлы.
Kamailio — интеграция с FreeSWITCH с примерами
Алгоритм работы Kamailio + FreeSWITCH
Предлагаю рассмотреть алгоритм работы между абонентом => Kamailio (Frontend) => FreeSWITCH (backend, media сервер)
- Абонент А отправляет запрос INVITE для установления сеанса связи с Абонентом B на Kamailio
- Kamailio проверяет IP адрес источника, если IP адрес не подходит по условию (не является IP адресом FreeSWITCH) — отправить запрос INVITE на backend media сервер (на FreeSWITCH)
- FreeSWITCH выполняет обработку INVITE запроса согласно своему dialplan’у. В этот момент у нас появляется две ноги (legs) вызова. 1) A-leg т.е FreeSWTICH обрабатывает запрос от абонента А. 2) B-leg т.е FreeSWITCH пытается дозвонится до абонента B.
В случае, если абонент B поднял трубку — соединить абонентов между собой - B-leg от FreeSWITCH поступает обратно на Kamailio
- Kamailio пытается дозвонится до абонента B
Может показаться, что сейчас все немного запутано, но при виде трассировки все станет понятней.
Практическая реализация в Kamailio
Предлагаю начать реализацию вышеописанного алгоритма пошагово т.е начнем выполнять сверху вниз.
Для начала предлагаю посмотреть какие сетевые интерфейсы у меня есть на виртуальной машине:
Как видно, у меня есть два сетевых интерфейса: eth0 и eth1. Eth0 — интерфейс с публичным («белым») IP адресом, eth1 — интерфейс с приватным IP адресом.
Для обработки SIP запросов от абонентов мы будем использовать оба интерфейса.
Для Kamailio мы будем слушать два порта с разными IP. Для этого изменим kamailio.cfg и добавим в самый верх:
listen=udp:78.**.241.199:5060
listen=udp:172.19.0.2:5060
alias=udp:sip.ipcalls24.com:5060
Директива listen используется для того, чтобы указать Kamailio, какие порты и IP адреса слушать. В данном случае т.к пока мы работаем только с UDP, мы будем слушать внешний (публичный) IP адрес 78.**.241.199 с портом 5060 и внутренний IP адрес 172.19.0.2 с портом 5060 для работы с FreeSWITCH.
Директива alias указывает какое DNS имя назначено для сервера Kamailio. В данном случае мы будем слушать sip.ipcalls24.com с портом 5060.
Далее, чтобы Kamailio отправлял SIP запросы на FreeSWITCH через приватный сетевой интерфейс (eth1) необходимо указать параметр mhomed. По умолчанию значения = 0 (выключено). Для включения параметра необходимо в /etc/sysctl.conf разрешить форвардинг (передачу пакетов) между сетевыми интерфейсами:
net.ipv4.ip_forward = 1
После чего применить изменения выполнив команду из под root пользователя:
sysctl -p
После чего уже в конфигурационном файле kamailio.cfg необходимо разрешить форвардинг пакетов (датаграм) между сетевыми интерфейсами. Для этого в самом верху, после директив listen, alias необходимо включить параметр mhomed:
mhomed=1
А также добавим два флага:
#!define tswitch 1
#!define fswitch 2
Мы будем использовать флаги для того, чтобы помечать в какую сторону идет звонок. В данном случае tswitch — это вызов, который поступил на Kamailio от абонента, а fswitch — это запрос, который пришел уже от media сервера (FreeSWITCH).
И добавим параметр модуля RR (record_route) для отключения вставки tag’ов в заголовки Record-Route:
modparam("rr", "append_fromtag", 0)
Также замечу, что сейчас мы реализуем примитивную схему работы (один Kamailio сервер + один FreeSWITCH сервер), в следующей статье мы будет использовать модуль dispatcher который позволяет использовать множество media серверов.
На этом подготовительная часть закончена и теперь реализуем сам алгоритм. Для этого мы изменим пользовательский маршрут route[DIALOG] созданный в предыдущих статьих (Kamailio с нуля с примерами):
route[DIALOG] {
if (!src_ip == '172.19.0.2') {
setflag(tswitch);
if (is_method("INVITE")) {
route(AUTH);
handle_ruri_alias();
record_route();
route(RELAY);
}
} else {
setflag(fswitch);
if (is_method("INVITE")) {
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;
}
}
}
Красным цветом выделена измененная часть маршрута DIALOG. Давайте более подробно рассмотрим, что мы здесь делаем:
- Проверяем IP адрес с которого пришел SIP запрос. Если IP адрес не является IP адресом FreeSWITCH — это значит, что это запрос от абонента, поэтому мы авторизуем INVITE запрос с помощью маршрута AUTH (рассматривалось в третьей статье, обработка SIP вызова в Kamailio). Далее мы помечаем этот запрос флагом tswitch, который означает, что мы отправим этот запрос на media сервер FreeSWITCH
- Если же IP адрес является IP адресом FreeSWITCH — помечаем этот вызов флагом fswitch и выполняем стандартную обработку INVITE, как мы это делали в предыдущих статьях.
Теперь давайте посмотрим как изменился пользовательский маршрут route[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");
if (isflagset(fswitch)) {
t_relay();
}
if (isflagset(tswitch)) {
rewritehostport("172.19.0.2:5090");
t_relay();
}
}
В этом маршруте мы реализуем маршрутизацию согласно флагам. Все, что приходит с флагом tswitch — мы отправляем на media сервер FreeSWITCH с помощью функции rewritehostport. Эта функция выполняет перенаправление на указанный IP адрес и указанный порт, после чего выполняем отправку запросов с помощью функции t_relay(). IP адрес 172.19.0.2 — приватный IP адрес, на котором находится FreeSWITCH, порт 5090 — это порт, который слушает FreeSWITCH (настройка FreeSWITCH рассматривается ниже)
Все что приходит с флагом fswitch — мы обрабатываем точно также как и раньше с помощью Kamailio и функции t_relay
FreeSWITCH (FusionPBX) в качестве backend voip архитектуры
Сама установка занимает очень мало времени т.к разработчики FusinPBX предоставляют уже готовые установочные bash скрипты. Установка заключается в выполнении этих скриптов из под root (sudo) пользователя.
Установка FreeSWITCH в Linux дистрибутивах
FusionPBX на Debian 10 (buster), 9 (stretch), 8 (jessie)
Для установки на дистрибутивы семейства Debian необходимо скачать установочные скрипты командой:
wget -O - https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/debian/pre-install.sh | sh;
После чего выполнить установку, выполнив команду
cd /usr/src/fusionpbx-install.sh/debian && ./install.sh
Все действия необходимо выполнить из под пользователя root
FusionPBX в дистрибутиве CentOS 7
Необходимо скачать пакет wget через менеджер пакетов yum:
yum install wget -y
После чего, также как и на Debian, необходимо скачать установочные скрипты:
wget -O - https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/centos/pre-install.sh | sh
И также выполняем скачанные скрипты:
cd /usr/src/fusionpbx-install.sh/centos && ./install.sh
PostgreSQL при установке FusionPBX
По умолчанию устанавливается Postgres версии 9. Если вы хотите перейти на более новую версию базы данных (например для возможности нативной логической репликации которая появилась начиная с 10 версии), необходимо немного подправить установочные скрипты. Для этого мы отредактируем файл установки на примере CentOS 7, в Debian необходимо делать по аналогии.
На официальном сайте Postgres выбрать необходимую версию и дистрибутив. Ссылка на официальные postgres репозитории здесь. Для CentOS 7 ссылка на репозиторий, например 11 версии, следующая:
https://yum.postgresql.org/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
После чего отредактировать файл, которые находится в /usr/src/fusionpbx-install.sh/centos/resources/postgresql.sh
#yum -y install postgresql96-server postgresql96-contrib postgresql96 postgresql96-libs postgresql96-devel
И закоментировать саму строку установки пакетов postgres версии 9. Вместо этих строк мы добавляем следующий репозиторий (приведу пример для 11 версии):
rpm -ivh https://yum.postgresql.org/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
Ниже приводится мой файл для установки PostgreSQL 11 версии:
#!/bin/sh
#move to script directory so all relative paths work
cd "$(dirname "$0")"
#includes
. ./config.sh
. ./colors.sh
#send a message
verbose "Installing PostgreSQL 11"
#generate a random password
password=$(dd if=/dev/urandom bs=1 count=20 2>/dev/null | base64)
#included in the distribution
#rpm -ivh --quiet http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/pgdg-centos94-9.4-3.noarch.rpm
rpm -ivh https://yum.postgresql.org/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum -y update
yum -y install postgresql11-server
#send a message
verbose "Initalize PostgreSQL database"
#initialize the database
/usr/pgsql-11/bin/postgresql-11-setup initdb
#allow loopback
sed -i 's/\(host *all *all *127.0.0.1\/32 *\)ident/\1md5/' /var/lib/pgsql/11/data/pg_hba.conf
sed -i 's/\(host *all *all *::1\/128 *\)ident/\1md5/' /var/lib/pgsql/11/data/pg_hba.conf
#systemd
systemctl daemon-reload
systemctl restart postgresql-11
#move to /tmp to prevent a red herring error when running sudo with psql
cwd=$(pwd)
cd /tmp
#add the databases, users and grant permissions to them
sudo -u postgres /usr/pgsql-11/bin/psql -d fusionpbx -c "DROP SCHEMA public cascade;";
sudo -u postgres /usr/pgsql-11/bin/psql -d fusionpbx -c "CREATE SCHEMA public;";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE DATABASE fusionpbx";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE DATABASE freeswitch";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE ROLE fusionpbx WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE ROLE freeswitch WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE fusionpbx to fusionpbx;"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to fusionpbx;"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to freeswitch;"
#ALTER USER fusionpbx WITH PASSWORD 'newpassword';
cd $cwd
#send a message
verbose "PostgreSQL 11 installed"
После изменения замены файла, необходимо выполнить скрипт установки install.sh:
cd /usr/src/fusionpbx-install.sh/centos && ./install.sh
После этого, будет установлена база данных Postgres версии 11.
Но вполне вероятно, что у вас уже будет установленна БД т.к с Kamailio мы работает через PostgreSQL. Поэтому для того, что бы не устанавливать занового Postgres, необходимо удалить файл с установкой БД:
rm -rf /usr/src/fusionpbx-install.sh/centos/resources/postgresql.sh
После этого достаточно выполнить несколько SQL запросов из под root пользователя:
sudo -u postgres /usr/pgsql-11/bin/psql -d fusionpbx -c "DROP SCHEMA public cascade;";
sudo -u postgres /usr/pgsql-11/bin/psql -d fusionpbx -c "CREATE SCHEMA public;";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE DATABASE fusionpbx";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE DATABASE freeswitch";
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE ROLE fusionpbx WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres /usr/pgsql-11/bin/psql -c "CREATE ROLE freeswitch WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE fusionpbx to fusionpbx;"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to fusionpbx;"
sudo -u postgres /usr/pgsql-11/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to freeswitch;"
#ALTER USER fusionpbx WITH PASSWORD 'newpassword';
В остальном же, установка остается одинаковой.
Настройка FreeSWITCH (FusionPBX) для работы с Kamailio
Для интеграции с Kamailio мы создадим отдельный SIP профиль в FusionPBX. Для этого необходимо зайти в web интерфейс FusionPBX (IP адрес для входа, логин и пароль вы увидете после успешного выполнения установочных скриптов). Страница входа будет выглядить следующим образом:
Вводим логин/пароль полученный после установки (логин admin) и видим сверху navbar:
Создание SIP профиля для интеграции с Kamailio
Мы создадим отдельный SIP профиль для работы с Kamailio, для этого в верхнем меню выберем вкладку Advanced, и перейдем в SIP Profile
После чего перейден в профиль external и нажмем «Copy»
И введем имя для нового SIP профиля, например softswitch.
После чего изменим порт который будет слушать FreeSWITCH, например на 5090. Для этого откроем созданный SIP профиль (softswitch) и следующие строки:
В параметре sip-ip у меня указан локальный IP адрес виртуальной машины (сервера), в sip-port задается порт, который будет слушать FreeSWITCH (в данном случае будет слушать порт 5090). После изменение параметров, необходимо сохранить изменения (SAVE) и перезагрузить SIP профиль (это сдалем позднее, после добавления ACL)
Создание ACL в FusionPBX
Для нового SIP профиля мы также создадим новый ACL, это делается во вкладке Advanced — Access Controls:
И нажнем на кнопку «Add» в верхнем правом углу, приведем создаваемый ACL к следующему виду:
Где по умолчанию мы запрещаем весь SIP трафик, но разрешаем с IP адреса 172.19.0.2 (внутренний IP адрес моей виртуальной машины) и с IP 78.**.241.199 — внешний IP адрес моей виртуальной машины. Необходимо изменить параметры на ваши IP адреса. Можно указывать IP адреса с помощью маски подсети, но в данном случае я этого не делаю т.к у меня пока что один сервер Kamailio.
Задаем имя, например SBC и нажимаем сохранить, кнопка «SAVE»
Теперь вернемся в созданный ранее SIP профиль «softswitch» и изменим используемый ACL для этого профиля, для этого в строке apply-inbound-acl укажем созданный нами ACL:
После чего сохраняем настройки SIP профиля с помощью кнопки SAVE.
Далее, чтобы применить изменения для SIP профиля, его необходимо перезагрузить. Делается это через navbar (Status -> SIP Status):
И в открывшемся окне, нажимаем «Restart» для созданного SIP профиля:
Создание домена в FusionPBX
FreeSWITCH (и FusionPBX) поддерживает мультдоменность, но в рамках этой статьи у нас будет только один домен — ipcalls24.com (с таким же доменом, как и пользователи в Kamailio). Для создания домена необходимо перейти в Advanced -> Domains и нажать кнопку Add:
И укажем в поле Name название домена:
После чего, можно перейти в новосозданный домен нажав в верхний правый угол:
Dialplan для локальных вызовов через FreeSWITCH
Также необходимо создать dialplan для обработки входящих вызовов с Kamailio. Есть два варианта как это можно сделать:
- Добавить dialplan через WEB GUI (FusionPBX)
- Выполнить SQL запрос в базу данных
Мы будем использовать первый вариант, чтобы показать как это делается через графический интерфейс, но в будущем для экономии времени мы будем выполнять SQL запросы напрямую в базу данных
Итак, для создания нового dialplan необходимо в навигационном меню перейти в Dialplan->Inbound Routes:
После чего в верхнем правом угле нажать на кнопку ADD и нажать на кнопку ADVANCED. После чего заполним необходимые поля для нового dialplan’а:
Нажимаем сохранить и переходим в то же окно Inbound Routes и выберем созданный маршрут local_call и отредактируем его следующим образом:
Сохраняем изменения.
Примеры работы Kamailio + FusionPBX (FreeSWITCH), legs (ноги) вызова
Давайте теперь рассмотрим трассировки, чтобы наглядно увидеть работоспособность.
SIP Leg-A (От абонента к Kamailio)
Абонент А отправляет запрос INVITE для установления связи с абонентом B. Для этого Абонент А отправляет INVITE на Kamailio, назовем это ногой А (leg-a):
Сам INVITE запрос ничем не отличается от рассмотренных ранее в предыдущих статьях. Но Kamailio вместо того, чтобы сразу пытаться позвонить абоненту B отправляет INVITE запрос абонента А на media сервер FreeSWITCH (172.19.0.2 с портом 5090), стоит обратить внимание на то с какого интерфейса вызов уходит в сторону FreeSWITCH. Как видно, вызов уходит с IP 172.19.0.2 и порта 5060 (помните начало статьи?), это приватный интерфейс eth1 и с помощью mhomed=1 (параметр указанный в начале статьи) мы обрабатываем все SIP запросы между Kamailio и FreeSWITCH в приватной подсети с помощью приватного сетевого интерфейса.
Но что происходит дальше, когда вызов попадает на FreeSWITCH?
SIP Leg-B (От Kamailio к вызываемому абоненту)
Посмотрим на трассировку sngrep:
Как видно, появился второй вызов (второй INVITE). И стоит обратить внимание, что IP адрес с которого пришел этот INVITE является IP адресом FreeSWITCH. Т.е это Leg-B (вызов от FreeSWITCH вызываемому абоненту B).
Давайте более подробно посмотрим на этот вызов:
Как видно из трассировки, вызов от FreeSWITCH на Kamailio обработал корректно, т.е Kamailio понял, что это вызов с его media серверов, поэтому он начал вызывать абонента B (с помощю функции lookup(«location»).
Также хочу обратить внимание на то, что вызов с FreeSWITCH пришел сразу на публичный IP адрес Kamailio. Это произошло из-за того, что в dialplan’е FreeSWITCH указано DNS имя sip.ipcalls24.com, а т.к у меня не настроены внутренние DNS сервера — поэтому sip.ipcalls24.com был орезовлен через внешние DNS сервера и именно поэтому вызов пришел сразу на публичный сетевой интерфейс Kamailio. Это можно исправить, если в dialplan local_call изменить sip.ipcalls24.com на приватный IP адрес Kamailio (172.19.0.2:5060):
Как видно, сейчас все работает коректно и все взаимодействие между Kamailio (frontend) и FreeSWITCH (backend) происходит с поомщью приватной подсети.
Заключение
В этой статье мы реализовали работу Kamailio + FreeSWITCH (FusionPBX), по аналогии можно выполнить интеграцию с Asterisk или любым другим softswitch 5 класса.
В следующей статье мы более подробно рассмотрим балансировку нагрузки с помощью модуля dispatcher.
Если возникают вопросы — можете задавать их в комментариях, по возможности постараюсь помочь!
Если есть желание сделать пожертвование, то это можно сделать кликнув по ссылке или же нажав на кнопку. Спасибо за поддержку !)
2 ответа к “Kamailio часть 7. FreeSWITCH (FusionPBX) интеграция в роли media (backend) сервера с примерами”
Добрый день,
очень познавательно и подробно) . А пробовали настраивать kamailio и pbx за натом, когда физически внешний айпи где-то на роутере?
Вот у меня такие интерфейсы получились
listen=udp:10.130.0.23:5070 #локалка
listen=udp:10.130.0.23:5060 advertise 178.0.0.169:5060 #интернет
mhomed = 1
регистрация и INVITE от клиента из интернета пробрасывается с одного сокета на другой. А INVITE от pbx заходит в udp:10.130.0.23:5070 и выходит с этого же сокета из-за ошибки (pv [pv_branch.c:62]: pv_get_branchx_helper(): error accessing branch [0])
в роуте RELAY:
if ($Ru == «udp:10.130.0.23:5070») {
#set_send_socket(«udp:10.130.0.23:5060»);
$fs = «udp:10.130.0.23:5060»;
#force_send_socket(«udp:10.130.0.23:5060»);
} else {
#set_send_socket(«udp:10.130.0.23:5070»);
$fs = «udp:10.130.0.23:5070»;
}
#$fs = «udp:10.130.0.23:5070»;
if (!t_relay()) {
sl_reply_error();
}
modparam(«rr», «append_fromtag», 0) НЕ ЗАГРУЖАЕТСЯ с 0 а с 1 норм
systemctl status kamailio.service
● kamailio.service — Kamailio (OpenSER) — the Open Source SIP Server
Loaded: loaded (/lib/systemd/system/kamailio.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Wed 2023-04-19 14:46:29 +05; 7s ago
Docs: man:kamailio(8)
Process: 13600 ExecStart=/usr/sbin/kamailio -P /run/kamailio/kamailio.pid -f $CFGFILE -m $SHM_MEMORY -M $PKG_MEMORY (code=exited, status=255/EXCEPTION)
CPU: 83ms
Apr 19 14:46:29 kama /usr/sbin/kamailio[13608]: DEBUG: tm [t_funcs.c:94]: tm_shutdown(): done
Apr 19 14:46:29 kama /usr/sbin/kamailio[13608]: INFO: [core/sctp_core.c:53]: sctp_core_destroy(): SCTP API not initialized
Apr 19 14:46:29 kama /usr/sbin/kamailio[13608]: DEBUG: [core/mem/shm.c:288]: shm_destroy_manager(): destroying memory manager: q_malloc
Apr 19 14:46:29 kama /usr/sbin/kamailio[13608]: DEBUG: [core/mem/q_malloc.c:1185]: qm_shm_lock_destroy(): destroying the shared memory lock
Apr 19 14:46:29 kama /usr/sbin/kamailio[13608]: DEBUG: [core/mem/pkg.c:97]: pkg_destroy_manager(): destroying memory manager: q_malloc
Apr 19 14:46:29 kama systemd[1]: kamailio.service: Scheduled restart job, restart counter is at 5.
Apr 19 14:46:29 kama systemd[1]: Stopped Kamailio (OpenSER) — the Open Source SIP Server.
Apr 19 14:46:29 kama systemd[1]: kamailio.service: Start request repeated too quickly.
Apr 19 14:46:29 kama systemd[1]: kamailio.service: Failed with result ‘exit-code’.
Apr 19 14:46:29 kama systemd[1]: Failed to start Kamailio (OpenSER) — the Open Source SIP Server.