Виртуальная АТС. Часть 3: Переводим Asterisk на PjSIP без лишних телодвижений
В первой и второй частях цикла статей мы разобрались с установкой IP-АТС (IP-PBX) на работающий под управлением Ubuntu VPS от RuVDS и настройкой основных функций с использованием канального драйвера chan_sip. Этот подход считается устаревшим, и в будущих версиях Asterisk поддержка chan_sip будет прекращена. Вместо него лучше использовать открытую мультимедийную библиотеку PjSIP. Несмотря на кардинальные различия в файлах конфигурации, переход не так сложен, как может показаться на первый взгляд.
Что такое PjSIP?
Важно понимать, что PjSIP — не какой-то новый протокол, а целая библиотека для работы со стеком обеспечивающих голосовую связь протоколов: SIP, RTP, SDP, STUN и т.д. Она представляет собой целую кучу модулей, что нашло отражение в конфигурационном файле pjsip.conf (он заменяет традиционный sip.conf). Файл разделен на секции, а работает с ним преимущественно модуль res_pjsip, при этом каждая секция определяет конфигурацию некоторого объекта. Имена секций традиционно заключаются в квадратные скобки, а в секции обязательно присутствует определяющая ее тип конструкция «type =».
Типы секций могут быть следующими:
ENDPOINT — аналог пира в sip.conf, который определяет опции протокола SIP и взаимодействие с AOR, AUTH и TRANSPORT. Обязательно связана хотя бы с одной секцией AOR;
AOR — здесь описано, как связаться с ENDPOINT;
TRANSPORT — в этой секции описываются настройки протоколов транспортного уровня, веб-сокеты и методы шифрования (наподобие general в sip.conf). Может быть одной для разных ENDPOINT или уникальной для некоторой точки;
REGISTRATION — отвечает за исходящие регистрации, например, транки к провайдерам;
AUTH — содержит опции и полномочия для входящих и исходящих регистраций. С ней ассоциируются ENDPOINT и REGISTRATIONS;
IDENTIFY — здесь можно задать IP источника для ENDPOINT;
ACL — используется res_pjsip для контроля входящих соединений, не привязан к ENDPOINT;
DOMAIN_ALIAS — псевдонимы домена;
CONTACT — нужна, чтобы явно не указывать SIP URI в Dialplan;
System — системные опции;
Global — глобальные опции;
Имена секций в большинстве случаев могут быть произвольными, но, к примеру, ENDPOINT и AOR должны именоваться идентично с заголовком SIP URI.
Нашу справку трудно назвать исчерпывающей, поскольку за кадром осталось много интересных фишек, вроде PjSIP Configuration Wizard: пока речь идет о миграции на новую библиотеку малой кровью. С нюансами и тонкостями можно разобраться позднее.
Конвертируем sip.conf в pjsip.conf
Из-за модульности структура конфигурационного файла pjsip.conf размазана тонким слоем по множеству секций — она гораздо сложнее, чем у старого-доброго sip.conf. Разработчики Asterisk подумали о простых админах и создали сценарий для конвертации. Написан он на Python, и если вы собираете ПО из исходников, уже есть в дистрибутиве: в каталоге contrib/scripts/sip_to_pjsip/. Мы ставили Asterisk из входящего в репозиторий Ubuntu бинарного пакета, поэтому скрипты пришлось скачать с GitHub.
Хотя формат конфигурационных файлов для разных версий IP-PBX особо не менялся, лучше выбрать скрипты из установленной у вас версии Asterisk вместо последней по умолчанию — в нашем случае 16.2.
Версию Asterisk можно посмотреть в консоли IP-PBX с помощью команды core show version
Вам понадобятся все файлы на Python из каталога contrib/scripts/sip_to_pjsip/ в репозитории на GitHub. Их нужно сложить в локальный каталог, перейти в каталог с конфигами Asterisk (обычно /etc/asterisk) и запустить скрипт sip_to_pjsip.py с привилегиями суперпользователя. Основная его задача, прочитать входной файл sip.conf и сделать новый pjsip.conf (подробности доступны в wiki Asterisk).
Сценарий сделает pjsip.conf, а дальше вас ждет его ручная полировка. Если вы устанавливали Asterisk по нашим статьям, придется еще настроить загрузку модулей в /etc/asterisk/modules.conf и изменить в Dialplan (/etc/asterisk/extensions.conf) вызов приложения Dial.
Сделанный конвертером файл /etc/asterisk/pjsip.conf на практике оказался нерабочим:
;--
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Non mapped elements start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[general]
allowoverlap = no
[office]
call-limit = 2
[sipnet]
remotesecret = пароль
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Non mapped elements end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
--;
[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0
[sipnet]
type = aor
contact = sip:логин@sipnet.ru
[sipnet]
type = identify
endpoint = sipnet
match = sipnet.ru
[sipnet]
type = endpoint
context = sipnet-trunk
dtmf_mode = rfc4733
disallow = all
allow = alaw,ulaw
direct_media = no
from_user = логин
from_domain = sipnet.ru
aors = sipnet
[1001]
type = aor
max_contacts = 1
[1001]
type = auth
username = 1001
password = пароль
[1001]
type = endpoint
context = homeoffice
dtmf_mode = rfc4733
disallow = all
allow = ulaw
allow = alaw
allow = g729
allow = g723
allow = g726
allow = h261
allow = h263
allow = h264
allow = h263p
callerid = Директор <1001>
auth = 1001
outbound_auth = 1001
aors = 1001
[acl]
type = acl
permit = XXX.XXX.XXX.XXX
deny = 0.0.0.0/0.0.0.0
[1002]
type = aor
max_contacts = 1
[1002]
type = auth
username = 1002
password = пароль
[1002]
type = endpoint
context = homeoffice
dtmf_mode = rfc4733
disallow = all
allow = ulaw
allow = alaw
allow = g729
allow = g723
allow = g726
allow = h261
allow = h263
allow = h264
allow = h263p
callerid = Секретарь <1002>
auth = 1002
outbound_auth = 1002
aors = 1002
Синтаксис его понятен, подробности можно найти в wiki Asterisk. Чтобы довести конфигурационный файл до ума, потребуется ручная правка.
Исправленный /etc/asterisk/pjsip.conf (как и в sip.conf, в нем можно использовать шаблоны):
;===============TRANSPORT
[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0
;===============ACL
[acl]
type = acl
deny = 0.0.0.0/0.0.0.0
permit = XXX.XXX.XXX.XXX
;===============SIPNET TRUNK
[sipnet]
type = registration
transport = transport-udp
outbound_auth = sipnet
server_uri = sip:sipnet.ru
client_uri = sip:логин@sipnet.ru
retry_interval = 60
[sipnet]
type = auth
auth_type = userpass
password = пароль
username = логин
[sipnet]
type = aor
contact = sip:логин@sipnet.ru
[sipnet]
type = endpoint
transport = transport-udp
; Контекст должен быть прописан в Dialplan
context = sipnet-trunk
dtmf_mode = rfc4733
disallow = all
allow = alaw,ulaw
direct_media = no
from_user = логин
from_domain = sipnet.ru
outbound_auth=sipnet
aors = sipnet
[sipnet]
type = identify
endpoint = sipnet
match = sipnet.ru
;===============USER TEMPLATES
[endpoint-template](!)
type = endpoint
transport = transport-udp
context = homeoffice
dtmf_mode = rfc4733
disallow = all
allow = ulaw
allow = alaw
allow = g729
allow = g723
allow = g726
allow = h261
allow = h263
allow = h264
allow = h263p
[auth-template-userpass](!)
type = auth
auth_type = userpass
[aor-template-single-reg](!)
type = aor
; PjSIP допускает и множественные регистрации с одного аккаунта
max_contacts = 1
;===============User 1001
[1001](endpoint-template)
auth = auth1001
aors = 1001
callerid = Директор <1001>
[auth1001](auth-template-userpass)
username = 1001
password = пароль
[1001](aor-template-single-reg)
;===============User 1002
[1002](endpoint-template)
auth = auth1002
aors= 1002
callerid = Секретарь <1002>
[auth1002](auth-template-userpass)
username = 1002
password = пароль
[1002](aor-template-single-reg)
С шаблонами писанины стало меньше, но есть и более интересный способ упростить админу жизнь — Configuration Wizard. Пожалуй, это одна из самых удобных фишек PjSIP, с которой мы разберемся в следующей статье.
Загружаем модули
Переписываем Dialplan
Самая простая часть: достаточно заменить SIP на PJSIP в вызове приложения Dial. Пока мы немного изменили простейший тестовый Dialplan из предыдущей статьи, более сложными вещами займемся позднее.
[general]
static=yes
writeprotect=no
priorityjumping=no
autofallthrough=yes
clearglobalvars=no
; Контекст по умолчанию принято закрывать ради удобства и безопасности
[default]
exten => _X.,1,NoOp()
same => n,Busy()
same => n,HangUp()
; Определяем контекст homeoffice
[homeoffice]
; разрешаем внутренние звонки
exten => _1XXX,1,Dial(PJSIP/${EXTEN})
; звонки по России производим через SIPNET
exten => _.7XXXXXXXXXX,1,Dial(PJSIP/${EXTEN:1}@sipnet)
; Определяем контекст sipnet-trunk, разрешаем входящие звонки через SIPNET
[sipnet-trunk]
; входящие звонки рассмотрим в следующей статье
Переход с chan_sip на PjSIP оказался делом не особенно сложным, но требующим изрядной доли ручной работы. Автоматически конвертировать конфигурацию у нас не получилось: сценарий выдал неработоспособный вариант, который пришлось переписать вручную. В следующей статье мы разберем работу с Configuration Wizard и наконец расширим dialplan для приема входящих вызовов, организации конференций и решения других задач маршрутизации звонков.