16.06.2017

TLS. Kamailio. Asterisk. Настройка

Схема которую планировали реализовать:

Client1 —<TLS>— Kamailio —<UDP>— Asterisk —<UDP>— Kamailio —<TLS>— <Client2>

Что почитать перед тем как настраивать, это сразу несколько позиций, они обязательно вам пригодятся, ибо быстро сделать даже такую простую схему без понимания принципа не получится. А может и получится, но останутся дыры.

  1. Документация TLS модуля для Камалио 4.4 – прекрасно описывается как создать сертификат без получения и т.д.
  2. ssldump -Nn покажет обмен внутри протокола ssl кто кому какие сертификаты посылает
  3. howto kamailio and tls

Теперь непосредственно, то как работает TLS в двух словах.

  1. создать центра сертификации – CA
  2. подписать им сертификат для Камалио
  3. скопировать в подписанный сертификат приватный ключь, этот на всякий случай
  4. создать сертификат для sip-клиента
  5. скопировать приватный ключ в сертификат т.к. некоторые сип клиенты не имеют возможность для указания отдельно приватного ключа в настройках
  6. вынести конфигурацию tls в отдельный файл
  7. далее прописать в calist.pem сертификат CA. Это нужно чтобы камалио получив сертификат от клиента проверил его CA в своем списке и если СА в доверенных, то одобрить сертификат (при этом неважно откуда звонит клиент и какой домен (CN) указан в сертификате
  8. Повесить камалио на lts : listen:tls:ip_address:5061
  9. подключить к своему клиенту сертификат.
  10. клиент будет проверять сертификат поэтому, в виндоус нужно добавить в доверенные центры сертификации ваш сертификат CA, который вы создавали в самом начале.
  11. в сертификате Камалио обязательно должен быть указан CN соответствующий домену Камалио, либо его IP адресу если домена как такогово нет.
  12. В настройках камалио нужно оставить только verify_certificate = yes, require_certificate = no. Если поставить оба значения, то у меня ситуация выглядела так: Во время звонка при передаче ACK сообщения sip-клиент создавал новое соединение TLS и оно было отказывалось работать, потому, что не совпадали CN ожидаемые sip-клиентом и указанные в сертификате. Не стал разбираться дальше почему так происходит.

Настройка на данном этапе завершена, сертификат проверяется, т.е. использовать левый сертификат не получится.

15.06.2017

Восстановление базы MySQL из бинарных логов

Восстановление базы MySQL из бинарных логов
Простая статься на хабре, помогает быстренько восстановить утерянные insert в таблицы.

 

Восстановление базы данных из ib_logfile0

10.06.2017

SIPP Тестирование Asterisk

Решил провести тестирование Астериска на предмет максимального количества звонков.
Сразу скажу, у меня Астериск 1.4 и я просто посылаю на эхотест его, примеры эхотеста в астериска в sip.conf есть.

Чтобы провести тестирование нагрузки нам понадобится sipp

  1. yum install sipp
  2. копируем в локальный каталог сценарий с uac_pcap.xml из документации sipp
  3. копируем pcap файлы для астериск в каталог pcap текущей папки
  4. подбираем нужные параметры для sipp и должно работать

Здесь я опишу только 4 пункт:

в моем случае конфигурация рабочая выглядит так:

sipp -sf uac_pcap.xml -r 1 -mi 111.111.111.111 -i 111.111.111.111 -s 1005 222.222.222.222 -trace_msg -rtp_echo -d 5000

где
111.111.111.111 – внешний интерфейс вашей машины
222.222.222.222 – адрес астериска
1005 – номер для эхотеста
-d 5000 – пауза в 5 секунд (опционально)
и все погнали, в моей конфигурации сети, без особых проблем астериск успевает обслужить 50 вызовов в секунду и примерно 500 одновременных соединений, потом начинаются ретрансмиты сообщений.

9.06.2017

Memcached и ограничение соединений к нему

Столкнулся с тем, что клиенты получают ошибку вызванную тем что скрипты на сайте не могу подключиться к memcached

докопался до вот чего:

Огромное количество соединений к localhost остаются в состоянии time_wait в документации по memcached прекрасно сказано, что нужно проверить для увеличении производительности, а вот здесь был конкретный совет по поводу подвисших соединений:

Details of how to tune these variables are outside the scope of this document, but google for “Linux TCP network tuning TIME_WAIT” (or whatever OS you have) will usually give you good results. Look for the variables below and understand their meaning before tuning.

!THESE ARE EXAMPLES, NOT RECOMMENDED VALUES!
net.ipv4.ip_local_port_range = 16384 65534
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

Я внес в систему только параметр net.ipv4.ip_local_port_range сделав его в два раза больше и дело пошло. Полезной оказалась статься про мониторинг memcached в частности утилита memcache-top и команда netstat которая показала кол-во еще ожидающих закрытия соединений.

9.06.2017

Автозапуск Debian 6 и Centos 6.8

Запуск программ при старте Debian:

Добавление скрипта в автозагрузку:

Удаление скрипта из автозагрузки:

27.05.2017

О себе и реквизиты

Я рос хорошим мальчиком. Давным давно, работая в службе технической поддержки в небольшом интернет-провайдере, случилось так, что у моего коллеги не получилось настроить voip телефонию в компании и за дело взялся я. Это самая моя любимая работа разбираться в том, в чем я не разбираюсь. Если у меня появляется азарт в этом, то всё – ныряю с головой.

Так я начал работать с интернет-телефонией.

Нет нужды описывать мой жизненный путь, оно как правило ни к чему, только повздыхать да покритиковать стороннему наблюдателю – от чего я вас, дорогие друзья, избавлю.

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

И вот я уже не хочу тратить много времени на статью про себя, просто скажу работаю на компанию ООО “ЮКСИ”.

Реквизиты:

ООО «ЮКСИ»

sip: 74952284411@did.yooxy.ru

fb: facebook.com/erewin

Юр. Адрес: 454021, ул Молодогвардейцев д.58 оф.1

Почтовый адрес: 454902, ул. Гостевая 3, оф. 002

ОГРН 1127447003386

ИНН/КПП 7447206406/744701001

Директор Еремин Павел Сергеевич, действует на основании Устава.

Расчетный счет № 40702810590000016075

в ОАО «ЧЕЛЯБИНВЕСТБАНК»

БИК 047501779 ИНН 7421000200 КПП 745301001

Кор/счет 30101810400000000779

27.05.2017

Куда я попал?

Добро пожаловать в обновленную версию записной книжки voip-инженера.

Давно начал вести записи по поводу решений разных задач области ip-телефонии. И несмотря на то, что задача это достаточно занудна, лично для меня конечно, часто ей пользовался. Моя работа связана как правила с разными задачами, от программирования до развертывания нескольких серверов для телефонии.

Что можно здесь найти, в первую очередь конечно меня, как консультанта, но и заметки по решениям разных задач. Вот список рубрик в которых приходилось решать те или иные задачи и решения которых, находятся на старом адресе http://opensips-blog.yooxy.ru

  • Asterisk
  • SQL запросы
  • Unix вопросы
  • Безопасность
  • Городские номера
  • Готовые решения
  • Идеи
  • Проблемы в коде
  • Проблемы при настройке
  • Спорт
  • Гитара

 

3.02.2016

Ограничение вызовов для exten

Ограничение одновременных вызовов в Астериске . Смысл прост: Ищем каналы по выражению “116-” а потом считаем кол-во элементов в массиве.

same => n,GotoIf($[“${FIELDQTY(CHANNELS(${CALLERID(num)}-), )}” > “1”]?busy-custom,s,1)

3.02.2016

Asterisk. Annoucement. Dialplan. ChannelRedirect. Elastix. Part 2.

В предыдущей заметке описал решение с когда пользователь вручную набирает номер и воспроизводит, во время разговора, заранее записанное приветствие.

В этой заметке, мы несколько изменим решение, т.к. оказывается оно еще должно работать с роботом AMI которым пользователи активно пользуются.

Итак, для начала скрипт АМИ, которым осуществляется вызов:

Action: Login
Username: admin
Secret: werkjnfgopw

 

Action: Originate
ActionID: 12345
Channel: Local/117@tapi
Context: agent-custom
Exten: 12122396200
Priority: 1

Ранее смысл решения основывался на том, чтобы сохранить внутренний номер телефона, привязать к нему имя правильного канала (то куда позвонили) и собственно воспроизвести в этот канал файлик с названием “extension_x”.

Для реализации у нас в  файле extension_custom.conf:

Этот контекст ловит определенный номер формата #*00x  и записывает приветствие в файлик “extension_x”. Тут все просто.

[custom-record] ; Context for recording voice messages
exten => _#*00X,1,Noop(${EXTEN})
same => n,Set(CHANNEL(language)=ru)
same => “”n,Set(MONITOR_FILENAME=${DIR}${CALLERID(num)}_${EXTEN:4})
same”” => n,Playback(beep)
same => n,Answer
same => n,Record(${MONITOR_FILENAME}:wav)
same => n,Playback(vm-msgsaved)
same => n,Playback(${MONITOR_FILENAME})
same => n,Playback(vm-goodbye)

этот макрос выполняется когда мы совершаем вызов вручную в функцию Dial встроен код ‘M^(testsub)’ делалось это прям через веб инфтерфейс Эластикса.

[macro-testsub]
exten => s,1,NoOp(${CHANNEL})
exten => s,n,NoOp(${MYVAR})
exten => s,n,Set(DB(br/${MYVAR})=${CHANNEL})
exten => s,n,NoOp(${DB(br/${MYVAR})})

Этот контекст должен вызываться когда с ext приходит команда ##X что означает, что ext хочет воспроизвести приветствие.

[transfer-context]
exten => “” _##X,1,NoOp(${DB(br/${CALLERID(num)})})
exten”” => “”_##X,2,Set(DB(from_ext/last_ext)=${CALLERID(num)})
exten”” => “”_##X,3,ChannelRedirect(${DB(br/${CALLERID(num)})},play-context,${EXTEN},1)
exten”” => _##X,4,Playback(${DIR}${CALLERID(num)}_${EXTEN:-1}) ;
exten => _##X,5,Hangup()

А Это вспомогательный контекст, в него мы перескакием, когда собственно случается событие приоритета 3 в transfer-context. До сих пор не понятно, почему нельзя передать переменную через channelredirect – приходит ее сохранять в DB затем извлекать…

[play-context]
exten => _##X,1,noop(${DB(from_ext/last_ext)})
exten => _##X,2,Playback(${DIR}${DB(from_ext/last_ext)}_${EXTEN:-1}) ;
exten => _##X,3,Set(DB(from_ext/last_ext)=”0″)
exten => _##X,4,Hangup()

 

Итак, при вызове функции AMI Originate как я описал выше, вызывается два пользователя: внутренний 116 например и внешний внутренний вызывается в контексте [tapi]

[tapi]

exten => 116,1,Answer
exten => 116,n,SIPAddHeader(Alert-Info: Ring Answer)
exten => 116,n,NoOp(Context: agent)
exten => 116,n,Dial(SIP/116)
exten => 116,n,hangup

а затем вызывается контекст [agent-custom]: Это все делает тот AMI скрипт. В подчеркнутых строчках сохранение переменной какой внутренний номер позвонил и вызов макроса dialer-dial, который…

exten => _1XXXXXXXXXX,1,Answer

exten => _1XXXXXXXXXX,n,NoOp(Context: agent)
exten => _1XXXXXXXXXX,n,noop(CALLERID: “””${CALLERID(all)})
exten””” => _1XXXXXXXXXX,n,NoOp(EXTEN: ${EXTEN})
exten => _1XXXXXXXXXX,n,noop(SIP_to: ${SIP_HEADER(TO)})
exten => _1XXXXXXXXXX,n,noop(SIP_from: ${SIP_HEADER(FROM)})
exten => _1XXXXXXXXXX,n,Set(__FROM_EXT=${CHANNEL(name)})
exten => _1XXXXXXXXXX,n,Dial(SIP/t1/991${EXTEN},60,r^M(dialer-dial))
exten => _1XXXXXXXXXX,n,noop(DIALSTATUS= ${DIALSTATUS})
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “BUSY”]?busy-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “CONGESTION”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “CHANUNAVAIL”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “NOANSWER”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,hangup

 

который… сохранит для нас связку внутреннего номера и канала который мы вызываем  в базу Астериска.

[macro-dialer-dial]

exten => s,1,NoOp(${FROM_EXT:6:3})
exten => s,n,Set(MYVAR_A=${FROM_EXT:6:3})
exten => s,n,Set(DB(br/${MYVAR_A})=${CHANNEL})

26.12.2015

Asterisk. Annoucement. Dialplan. ChannelRedirect. Elastix.

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

Задача: Записать заранее приветствие в нескольких вариантах, и затем воспроизводить его абонентку по нажатию на кнопки быстрого набора.

Проблема: Быстрый набор – это просто очередной INVITE с заранее запрограммированным кодом, поэтому, для астериска это выглядит как поступление нового звонка.

Решение: Я решил эту задачу как для DTMF так и для использования speed-dial.

Ключ к решению: Основная проблема это заставить астериск воспроизводить заранее записанное приветствие нужному абоненту, если просто внести в dialplan запись вида ##1, где номер приветствия то астериск его проиграет тому кто прислал вызов, а нам нужно воспроизвести его абоненту, да еще и в терминологии Asterisk в другой канал.

Коротенько: Записывать приветствия мы будем формата XXXX_N.wav , XXXX- номер эксентшина который записывает приветствие, а N – это номер самого приветствия, таким образом мы можем записывать несколько приветствий для одного экстеншина.

[custom-record] ; Context for recording voice messages
exten => _#*00X,1,Noop(${EXTEN})
same => n,Set(CHANNEL(language)=ru)
same => “”n,Set(MONITOR_FILENAME=${DIR}${CALLERID(num)}_${EXTEN:4})
same”” => n,Playback(beep)
same => n,Answer
same => n,Record(${MONITOR_FILENAME}:wav)
same => n,Playback(vm-msgsaved)
same => n,Playback(${MONITOR_FILENAME})
same => n,Playback(vm-goodbye)

Воспроизведение:  При звонке мы сохраняем данные кто звонил и имя канала куда позвонили   : ${CALLEDRID(num)} – ${CHANNEL}, Делать это нужно в время команды Dial поэтому используется pre_dial_handler, для Эластикс это просто Опции в разделе General Setting. Tt – дает возмоножсть пользоваться DTMF, а ^M(testsub) выполняет макрос testsub.

[macro-testsub]
exten => s,1,NoOp(${CHANNEL})
exten => s,n,NoOp(${MYVAR})
exten => s,n,Set(DB(br/${MYVAR})=${CHANNEL})
exten => s,n,NoOp(${DB(br/${MYVAR})})

Чтобы запомнить переменную кто звонил мы добавляем строчку “exten => s,n,Set(__MYVAR=${CALLERID(number)})”  в macro-user-callerid и отправляем этот макрос в файл extensions_override_elastix Это означает, что Эластикс будет читать его их этого файла, чтобы не случилось в конфигурации. __ – означает что эта переменная будем передана вновь создаваемому каналу командой Dial.

И теперь при получении кода ##N от пользователя мы просто смотрим есть такая переменная в DB Астериска, если есть, то переводим сохраненный ранее канал в место где начинается воспроизведение.

[transfer-context]
exten => “”_##X,1,NoOp(${DB(br/${CALLERID(num)})})
exten”” => “”_##X,2,ChannelRedirect(${DB(br/${CALLERID(num)})},transfer-context,${EXTEN},3)
exten”” => _##X,3,Playback(${DIR}${MYVAR}_${EXTEN:-1}) ;
exten => _##X,4,Hangup()

Таким образом никаких скриптов, все гибко, красиво, изящно.

Бай.

 

PS: http://asteriskfaqs.org/2010/09/09/asterisk-users/set-channel-variable-from-within-other-channel.html

PS: https://wiki.asterisk.org/wiki/display/AST/Function_DB