воскресенье, 7 февраля 2016 г.

Использование поля Diversion в SIP пакете и возможные решения проблемы недозвона при двойной переадресации в Asterisk.

Справка о использовании поля Diversion, а так же описание переадресации в SIP здесь: https://tools.ietf.org/html/rfc5806

При двойной переадресации вызова в Asterisk могут быть проблемы недозвона. Например, имеем схему:


Запрос INVITE, поступающий на Asterisk будет такой:
INVITE sip:2057004@10.200.104.12:5172;user=phone SIP/2.0
Accept: application/sdp;q=0.3
Accept: application/ISUP;q=0.2
Accept: multipart/mixed;q=0.1
Allow: INVITE,ACK,CANCEL,BYE,INFO,PRACK,UPDATE,OPTIONS,REGISTER,REFER,SUBSCRIBE,MESSAGE,PUBLISH
Call-ID: 41a5c682bgw882nql@10.200.101.11
Contact: "3303030" <sip:3303030@10.200.101.11:5060;user=phone>
CSeq: 298 INVITE
Expires: 3600
From: "3303030" <sip:3303030@10.200.101.11:5060;user=phone;cpc=ordinary>;tag=t8uu0piu60
To: "205704" <sip:2057004@10.200.104.12:5172;user=phone>
Organization: IskraTel
User-Agent: SI3000
Via: SIP/2.0/UDP 10.200.101.11:5060;branch=z9hG4bK-8uhf0-9alb1
Max-Forwards: 69
Subject: Call from CS6111
Diversion:"910123000"<sip:9101230000@10.200.101.11:5060>;reason=unconditional;privacy=full;counter=1
Content-Length:  233
Content-Type: application/sdp
Content-Disposition: session;handling=required
На Asterisk поступает уже переадресованный вызов с мобильного телефона. Вызов приходит с полем Diversion, в котором указывается номер телефона, на котором установлена переадресация.  На Asterisk в extension.conf установлена переадресация на другой мобильный телефон. 


В этой схеме можем наблюдать проблемы недозвона. 
Проблема возникает из-за того, что некоторые операторы неадекватно реагируют на поле Diversion и могут отбросить вызов, если не смогут его тарифицировать.

Можно решить проблему глобально, выключив поддержку заголовка SIP Diversion глобально в sip.conf. Для этого в глобальных настройках sip.conf необходимо прописать:
send_diversion=no (по умолчанию «yes»). 
Обычно Asterisk отправляет Diversion хидеры с определенным SIP invites для ретрансляции данных о переадресованных звонках. Если выключено, Asterisk не будет отсылать Diversion хидеры, Отсылка будет только если их добавят вручную.
Таким образом, установив в sip.conf
send_diversion=no
и следующие правила переадресации в extensiom.conf
exten => 2057004,1,Set(CALLERID(all)="2057004" <2057004>)
exten => 2057004,n,Dial(${OUTTRUNKSI}/89276132605,120,r)
exten => 2057004,n,Hangup()
будем иметь нормально установленный вызов:


Выходящий с Asterisk запрос INVITE не будет иметь поле Diversion:
INVITE sip:89276132605@10.200.101.11 SIP/2.0.
Via: SIP/2.0/UDP 10.200.104.12:5172;branch=z9hG4bK50851d21.
Max-Forwards: 70.
From: "2057004" <sip:2057004@10.200.104.12:5172>;tag=as07f597ec.
To: <sip:89276132605@10.200.101.11>.
Contact: <sip:2057004@10.200.104.12:5172>.
Call-ID: 26f878371ab6f22b7f68b67923197933@10.200.104.12:5172.
CSeq: 102 INVITE.
User-Agent: Asterisk PBX 13.1-cert2.
Date: Sun, 31 Jan 2016 15:01:25 GMT.
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE.
Supported: replaces, timer.
Content-Type: application/sdp.
Content-Length: 271.

Решить проблему исключения заголовка Diversion из исходящих INVITE запросов можно и локально для одного абонента, выключив глобальную настройку send_diversion, то есть установив send_diversion=yes.
При этом переадресацию в extension.conf нужно записывать вот так:
exten => 2057004,1,Set(CALLERID(all)="2057004" <2057004>)
exten => 2057004,n,Set(CALLERID(rdnis)=)
exten => 2057004,n,Dial(${OUTTRUNKSI}/89276132605,120,r)
exten => 2057004,n,Hangup()

При необходимости можно добавить второе поле Diversion в SIP пакет INVITE. Это делается так:
exten => 2057004,1,Set(CALLERID(all)="2057004" <2057004>)
exten => 2057004,n,SIPAddHeader(Diversion: <sip:2057004@10.200.104.12>\;user=phone\;privacy=off\;reason=unconditional)
exten => 2057004,n,Dial(${OUTTRUNKSI}/89276132605,120,r)
exten => 2057004,n,Hangup()
Здесь:
privacy=off -пераметр указывает удаленной стороне не верить полю Remote-Party-ID
reason=unconditional - причина перевода (reason) - безусловная переадресация (unconditional)
Пакет INVITE, ходящий с Asterisk будет такой:
INVITE sip:89276132605@10.200.101.11 SIP/2.0.
Via: SIP/2.0/UDP 10.200.104.12:5172;branch=z9hG4bK36d97725.
Max-Forwards: 70.
From: "2057004" <sip:2059590@10.200.104.12:5172>;tag=as540b20bf.
To: <sip:89276132605@10.200.101.11>.
Contact: <sip:2057004@10.200.104.12:5172>.
Call-ID: 3471e7a82c066a3b34ed1aee4a4bd18c@10.200.104.12:5172.
CSeq: 102 INVITE.
User-Agent: Asterisk PBX 13.1-cert2.
Date: Sun, 31 Jan 2016 15:13:33 GMT.
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE.
Supported: replaces, timer.
Diversion: <sip:2057004@10.200.104.12>;user=phone;privacy=off;reason=unconditional.
Diversion: "9101230000" <sip:9101230000@10.200.104.12>;reason=unconditional.
Content-Type: application/sdp.
Content-Length: 270.





Предварительная настройка ASTERISK 13, которая пригодиться всегда.


1 Делаем резервные копии базовых конфигурационных файлов
# cp /etc/asterisk/sip.conf  /etc/asterisk/sip.conf.sample
# cp /etc/asterisk/extensions.conf /etc/asterisk/extensions.conf.sample

2 Настройки RTP в Asterisk:
В файле /etc/asterisk/rtp.conf в секции [general]
разрешаем хождение RTP по портам от 8000 до 20000
# nano /etc/asterisk/rtp.conf
[general]
rtpstart=8000
rtpend=20000
Рестартуем asterisk
# systemctl restart asterisk.service

3 Отключение сбора CDR в CSV файл.
CSV файл обычно располагается здесь: /var/log/asterisk/cdr-csv/Master.csv
Что бы посмотреть, что данный CDR файл используется для записи информации о вызове, выполняем команду:
# tail -f /var/log/asterisk/cdr-csv/Master.csv
и совершаем вызов через asterisk. В консоли мы должны увидеть новую CDR запись.
Для отключения записи логов CDR в файл Master.csv
 -  закомментировать секцию [csv] в файле /etc/asterisk/cdr.conf
# nano /etc/asterisk/cdr.conf
Должно получиться:
;[csv]
;usegmtime=yes    ; log date/time in GMT.  Default is "no"
;loguniqueid=yes  ; log uniqueid.  Default is "no"
;loguserfield=yes ; log user field.  Default is "no"
;accountlogs=yes  ; create separate log file for each account code. Default is "yes"
 -  Убрать из загрузки модуль модуль cdr_csv.so в /etc/asterisk/modules.conf:
# nano /etc/asterisk/modules.conf
Добавляем к конец строчку:
noload =>cdr_csv.so
 -  Рестартуем сервис Asterisk
# systemctl restart asterisk.service

4 Настаиваем сбор CDR файлов сервера asterisk в базу данных MySQL
Коннектимся к MySQL
# mysql -uroot -p
Создаем базу данных
> CREATE DATABASE asterisk;
Подключаемся к базе данных
> USE asterisk;
Создаем таблицу
> CREATE TABLE IF NOT EXISTS `cdr` (
  `calldate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `clid` varchar(80) NOT NULL DEFAULT '',
  `src` varchar(80) NOT NULL DEFAULT '',
  `dst` varchar(80) NOT NULL DEFAULT '',
  `dcontext` varchar(80) NOT NULL DEFAULT '',
  `channel` varchar(80) NOT NULL DEFAULT '',
  `dstchannel` varchar(80) NOT NULL DEFAULT '',
  `lastapp` varchar(80) NOT NULL DEFAULT '',
  `lastdata` varchar(80) NOT NULL DEFAULT '',
  `duration` int(11) NOT NULL DEFAULT '0',
  `billsec` int(11) NOT NULL DEFAULT '0',
  `disposition` varchar(45) NOT NULL DEFAULT '',
  `amaflags` int(11) NOT NULL DEFAULT '0',
  `accountcode` varchar(20) NOT NULL DEFAULT '',
  `uniqueid` varchar(32) NOT NULL DEFAULT '',
  `remote_signal_ip` varchar(17) CHARACTER SET utf8 NOT NULL,
  `userfield` varchar(255) NOT NULL DEFAULT '',
  `did` varchar(50) NOT NULL DEFAULT '',
  `recordingfile` varchar(255) NOT NULL DEFAULT '',
  `cnum` varchar(40) NOT NULL DEFAULT '',
  `cnam` varchar(40) NOT NULL DEFAULT '',
  `outbound_cnum` varchar(40) NOT NULL DEFAULT '',
  `outbound_cnam` varchar(40) NOT NULL DEFAULT '',
  `dst_cnam` varchar(40) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Создаем индексы для этой таблицы:
> ALTER TABLE `cdr`
  ADD KEY `calldate` (`calldate`),
  ADD KEY `dst` (`dst`),
  ADD KEY `accountcode` (`accountcode`),
  ADD KEY `uniqueid` (`uniqueid`),
  ADD KEY `did` (`did`);
Создаем пользователя asterisk  с паролем passwd123
> grant all on asterisk.cdr to asterisk@'localhost' identified by 'passwd123';
Выходим из MySQL.

Сбор CDR в базу MySQL нужно выполнять через сервис ODBC.
Выполняем установку сервиса ODBC, если того еще не произошло раньше:
# yum install unixODBC unixODBC-devel libtool-ltdl libtool-ltdl-devel mysql-connector-odbc
Редактируем файл драйверов сервиса ODBC /etc/odbcinst.ini
# nano /etc/odbcinst.ini
В файле не закомментированным должна быть только секция [MySQL]
[MySQL]
Description    = ODBC for MySQL
Driver         = /usr/lib64/libmyodbc5.so
Setup          = /usr/lib64/libodbcmyS.so
FileUsage      = 1
Проверим видит ли ODBC MySQL драйвер
# odbcinst -q -d
[MySQL]
Создаем файл учетных данных ODBC /etc/odbc.ini
# nano /etc/odbc.ini 
Выписываем туда следующее содержимое:
[MySQL-asteriskcdrdb]
Description=MySQL connection to 'asterisk' database
Driver=MySQL
Server=localhost
Database=asterisk
Port=3306
Socket=/var/lib/mysql/mysql.sock
UserName=asterisk
Password=passwd123
Здесь:
MySQL-asteriskcdrdb – наименование соединения с сервером MySQL
UserName – имя пользователя asterisk
Password – пароль пользователя asterisk

Правим конфигурационные файлы Asdterisk
Проверяем файл  /etc/asterisk/cdr.conf
# nano /etc/asterisk/cdr.conf
В файле должен быть раскомментирован в секции [general] главный параметр enable
[general]
enable=yes
Вносим изменения в файл настройки сервиса ODBC
# nano /etc/asterisk/res_odbc.conf
Вписываем в конец файла:
[asteriskcdrdb]
enabled=>yes
dsn=>MySQL-asteriskcdrdb
pooling=>no
limit=>5
pre-connect=>yes
username=>asterisk
password=>passwd123
Связываем настройку ODBC Asterisk с задачей сбора CDR.
Редактируем файл /etc/asterisk/cdr_odbc.conf
# nano /etc/asterisk/cdr_odbc.conf
Вписываем в конец файла:
[asteriskcdrdb]
connection=asteriskcdrdb
loguniqueid=yes
table=cdr
alias start => calldate

Для того, что бы в базу данных MySQL в поле remote_signal_ip  складывались ip адреса вызывающих абонентов, в диал-план в файле /etc/asterisk/extensions.conf в план набора надо написать вот эту строчку в тот контекст, при совершении вызова через который котором нужно сохранять IP адрес:
Set(CDR(remote_signal_ip)=${CHANNEL(recvip)});

Рестартуем Asterisk:
# systemctl restart asterisk.service
Теперь база данных должна пополняться вызовами, проходящими через asterisk.

Проверка работы ODBC:
asterisk2*CLI> odbc show
ODBC DSN Settings
-----------------
Name:   asteriskcdrdb
DSN:    MySQL-asteriskcdrdb
   Last connection attempt: 1970-01-01 03:00:00
Pooled: No
Connected: Yes

5. Настройка сбора логов CEL(Channel Event Logging) в базу данных MySQL

Коннектимся к MySQL
# mysql -uroot -p
Подключаемся к базе данных asterisk
> USE asterisk;
Создаем таблицу cel
> CREATE TABLE IF NOT EXISTS `cel` (
`id` int(11) NOT NULL auto_increment,
`eventtype` varchar(30) NOT NULL,
`eventtime` datetime NOT NULL,
`cid_name` varchar(80) NOT NULL,
`cid_num` varchar(80) NOT NULL,
`cid_ani` varchar(80) NOT NULL,
`cid_rdnis` varchar(80) NOT NULL,
`cid_dnid` varchar(80) NOT NULL,
`exten` varchar(80) NOT NULL,
`context` varchar(80) NOT NULL,
`channame` varchar(80) NOT NULL,
`src` varchar(80) NOT NULL,
`dst` varchar(80) NOT NULL,
`channel` varchar(80) NOT NULL,
`dstchannel` varchar(80) NOT NULL,
`appname` varchar(80) NOT NULL,
`appdata` varchar(80) NOT NULL,
`amaflags` int(11) NOT NULL,
`accountcode` varchar(20) NOT NULL,
`uniqueid` varchar(32) NOT NULL,
`linkedid` varchar(32) NOT NULL,
`peer` varchar(80) NOT NULL,
`userdeftype` varchar(255) NOT NULL,
`eventextra` varchar(255) NOT NULL,
`userfield` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `uniqueid_index` (`uniqueid`),
KEY `eventtime_index` (`eventtime`),
KEY `linkedid_index` (`linkedid`)
);
Даем пользователю asterisk возможность работы с таблицей cel
> grant all on asterisk.cel to asterisk@'localhost' identified by 'passwd123';
Выходим из MySQL.

Включаем генерацию CEL в asterisk:
# nano /etc/asterisk/cel.conf
В разделе [general] должно быть прописано
[general]
enable=yes
apps=all
events=ALL
dateformat = %F %T
Затем указываем asterisk, куда заносить сгенерированные события CEL
# nano /etc/asterisk/cel_odbc.conf
В конец файла дописываем:
[cel]
connection=asteriskcdrdb
loguniqueid=yes
table=cel
Здесь cel – название таблицы в MySQL, asteriskcdrdb – название соединения ODBC из файла /etc/asterisk/res_odbc.conf

Перезагружаем сервис asterisk:
# systemctl restart asterisk.service

Проверка включения CEL в asterisk
# asterisk -rx "cel show status"
CEL Logging: Enabled
CEL Tracking Event: ALL
CEL Tracking Event: CHAN_START
CEL Tracking Event: CHAN_END
CEL Tracking Event: HANGUP
CEL Tracking Event: ANSWER
CEL Tracking Event: APP_START
CEL Tracking Event: APP_END
CEL Tracking Event: BRIDGE_ENTER
CEL Tracking Event: BRIDGE_EXIT
CEL Tracking Event: PARK_START
CEL Tracking Event: PARK_END
CEL Tracking Event: BLINDTRANSFER
CEL Tracking Event: ATTENDEDTRANSFER
CEL Tracking Event: USER_DEFINED
CEL Tracking Event: LINKEDID_END
CEL Tracking Event: PICKUP
CEL Tracking Event: FORWARD
CEL Tracking Event: LOCAL_OPTIMIZE
CEL Tracking Application: all
CEL Event Subscriber: ODBC CEL backend
CEL Event Subscriber: CEL Custom CSV Logging

6 Настройка обработчика джиттера в Asterisk.
Для того что бы включить компенсацию джиттера на сервере Asterisk в файл sip.conf в секцию [general] вписываем строки:
jbenable = yes
jbforce = yes
jbmaxsize = 400
jbresyncthreshold = 1000 
jbimpl = fixed
Затем из консоли Asterisk выполняем
> sip reload