Связь двух астерисков 02.06.2011

Мне понадобилось подключить по одному телефону в Подольске, Питере и Екатеринбурге к Москве, причем сделать их членами одной группы звонков.

По большому счету, можно было бы подключить телефоны напрямую к нашей станции через SIP, но мне не захотелось пробрасывать полный пул портов через наш и без того непростой роутер :) И я решил поднять три "региональных" астериска, связать их с центральным по IAX2 и посмотреть - что получится.

Настройка удаленной станции

С настройкой удаленной станции все несложно. Фактически, она будет работать в качестве ретранслятора.

Устанавливаем asterisk, asterisk.conf, asterisk.adsi, logger.conf, modules.conf, rtp.conf берем из изначальной конфигурации (или из "основного" астериска).

В файле sip.conf я прописал интерфейс, к которому будут подцеплены sip-телефоны, а так же, временно, вручную прописал пользователей. Описание настроек вы можете посмотреть в предыдущих статьях.

[general]
; привязаться к интерфейсу
bindaddr=10.62.3.1

context=internal_phones
allowoverlap=no        
bindport=5060          
srvlookup=no            
qualify=500            
rtcachefriends=yes      
limitonpeer=yes ; для очереди
busylevel=1

; Тестовый пользователь
[1111]
secret=пароль
host=dynamic
type=friend
disallow=all
allow=alaw,gsm,ulaw
allowsubscribe=yes
allowtransfer=yes
subscribecontext=sip_subscribe
busylevel=1
callcounter=yes
callerid=1111 Test user

Дальше создадим файл iax.conf и в нем пропишем наш основной астериск. Так как оба сервера имеют статические внешние IP (точнее, на основном просто проброшен IAX-порт), то сам файл не нуждается в конструкции register

[general]
; пересоздавать, если нет ответа в течение
; 2 секунд
autokill=yes
qualify=2000
disallow=all;
allow=alaw;gsm;ulaw;

; идентификатор подключения
[mainserv]
qualify=2000
; адрес удаленного (основного) сервера
host=1.2.3.4
; контекст, куда будут попадать звонки с удаленного
; сервера
context=iax2_incoming
; тип означает, что можно и принимать и передавать звонки
; на этот сервер
type=friend
; имя пользователя, которое укажет удаленный сервер при
; подключении
username=podolsk
; пароль
secret=пароль
; и небольшое дополнительное ограничение по IP
deny=0.0.0.0/0.0.0.0
permit=1.2.3.4/255.255.255.255

Диалплан тоже не блещет особыми изысками, т.к. вся основная работа переложена на удаленный сервер. extensions.conf:

; звонок с внутреннего телефона.
; Пока что польностью перебросим на основную АТС
; в контекст internal_phones
switch => IAX2/mainserv/internal_phones

[iax2_incoming]
; контекст, куда приходят звонки с основной станции
; сразу набираем номер через SIP, считая что он правильный
exten=> _XXXX,1,Dial(SIP/${EXTEN},,gt)

[sip_subscribe]
; контекст для обработки статуса телефона
exten=> 1291,hint,SIP/1291

Основная АТС

У основной АТС, во-первых, пропишем в iax.conf наш удаленный мини-сервер:

; имя IAX2-контекста
[podolsk]
qualify=2000
; адрес удаленного хоста
host=5.6.7.8
; контексты, в который могут попадать вызовы с хоста
context=iax2_incoming
context=internal_phones
; тип - и отправляем, и принимаем звонки
type=friend
; пароль
secret=Суперпароль
; имя, с которым будем там регистрироваться
username=mainserv
; запрещаем прием звонков отовсюду, кроме нужных IP
deny=0.0.0.0/0.0.0.0
permit=5.6.7.8/255.255.255.255

Теперь поменяем диалплан. Так как набираемые номера для системы должны быть как обычные внутренние, то я модифицировал макрос набора внутренних SIP номеров (extensions.conf):

[macro-dial_internal_number]
; макрос набора внутреннего, 4-х значного номера.
exten => s,1,NoOp(dial_internal_number: ${ARG1})
; при помощи нашего скрипта получим номер, которуый нужно набрать
exten => s,n,SET(NUMBER_TO_DIAL=${SHELL(php /var/lib/asterisk/rittal/get_number.php -n=${ARG1})})
; если вернулся пустой номер
exten => s,n,GotoIf($["${NUMBER_TO_DIAL}" = ""]?wrong_number)

; номер непустой.
; если он имеет префикс SIP_, то отправляем его на набор SIP.
exten => s,n,GotoIf($["${NUMBER_TO_DIAL:0:4}" = "SIP_"]?dial_sip_number)
; если он имеет префикс CON_, то набираем этот контекст.
exten => s,n,GotoIf($["${NUMBER_TO_DIAL:0:4}" = "CON_"]?dial_context)
; если он имеет префикс DAH_, то набираем аналоговый номер.
exten => s,n,GotoIf($["${NUMBER_TO_DIAL:0:4}" = "DAH_"]?dial_dahdi)
; если он имеет префикс INT_, то набираем удаленный SIP.
exten => s,n,GotoIf($["${NUMBER_TO_DIAL:0:4}" = "INT_"]?dial_sip_remote)

; это внешний номер. отправляем его на соответствующий набор.
exten => s,n,Macro(dial_external_number,${NUMBER_TO_DIAL})
exten => s,n,MacroExit()


; вызываем макрос набора sip-номера, отрезая первые 4 символа (SIP_)
exten => s,n(dial_sip_number),Macro(dial_sip_number,${NUMBER_TO_DIAL:4})
exten => s,n,MacroExit()

exten => s,n(dial_context),Dial(Local/${NUMBER_TO_DIAL:4},,g)
exten => s,n,MacroExit()

exten => s,n(dial_dahdi),Dial(DAHDI/${NUMBER_TO_DIAL:4},,g)
exten => s,n,MacroExit()

; набираем sip-номер на удаленном asterisk
exten => s,n(dial_sip_remote),Set(NUMBER_TO_DIAL=${NUMBER_TO_DIAL:4})
exten => s,n,SET(REMOTE_STANTION=${CUT(NUMBER_TO_DIAL,/,1)})
exten => s,n,SET(NUMBER_TO_DIAL=${CUT(NUMBER_TO_DIAL,/,2)})
exten => s,n,Macro(dial_sip_number,${NUMBER_TO_DIAL},${REMOTE_STANTION})
exten => s,n,MacroExit()

; неверный номер - сообщаем и кладем трубку
exten => s,n(wrong_number),Playback(rittal/number_is_wrong)
exten => s,n,MacroExit()


[macro-dial_sip_number]
; набираем номер на sip-телефоне. здесь надо будет добавить логику переадресаций
; на голосовую почту и тд.
; в ARG2 содержится название удаленной станции asterisk
exten => s,1,NoOp(dial_sip_number: ${ARG1})

; проверим набираемый номер на занятость
; по входящим
exten => s,n,GotoIF($[${GROUP_COUNT(${ARG1}@busy_in)} > 0 ]?number_is_busy)
; и исходящим
exten => s,n,GotoIF($[${GROUP_COUNT(${ARG1}@busy_out)} > 0 ]?number_is_busy)

; маркируем набираемый как "занято"
; набирающий уже маркирован
exten => s,n,Set(GROUP(busy_in)=${ARG1})

; Если номер недоступен (отключен) переходим к метке
; номер не существует number_not_connected
; exten => s,n,GotoIf($["${SIPPEER(${ARG1},status):0:2}" = "UN"]?number_not_connected)

exten => s,n,GotoIF($["${ARG2}" = ""]?sip_local:sip_remote)

; наберем номер на удаленной iax2-станции
exten => s,n(sip_remote),Dial(IAX2/${ARG2}/${ARG1},,gt)
exten => s,n,Goto(dial_finished)

; наберем номер через SIP, используя переменную {ARG1}
; после того, как повесят трубку, мы продолжим диалплан
exten => s,n(sip_local),Dial(SIP/${ARG1},,gt)
exten => s,n,Goto(dial_finished)


exten => s,n(dial_finished),NoOp(DialStatus: ${DIALSTATUS})
; ксли номер занят
exten => s,n,GotoIF($["${DIALSTATUS}" = "CHANUNAVAIL" ]?number_is_busy)

; в конце - выйдем из макроса
exten => s,n,MacroExit()

; обработка ошибок

; номер есть, но аппарат не подключен
exten => s,n(number_not_connected),Playback(rittal/number_is_not_connected)
exten => s,n,MacroExit()

; номер занят
exten => s,n(number_is_busy),Playback(rittal/number_is_busy)
exten => s,n,MacroExit()

В файл скрипта get_number.php я добавил дополнительный вариант, когда для набора удаленных номеров возвращается строка вида INT_iaxсервер/номер (например, INT_podolsk/1111).

Если вы все настроили правильно, то пробуйте звонить и наслаждайтесь результатом!

В следующей статье я расскажу о том, как отслеживать статус удаленного телефона - это целая песня!