Как стать автором
Обновить

Freeswitch: по пути наименьшего сопротивления

Время на прочтение 13 мин
Количество просмотров 68K

Немного лирики


Сколько помню себя в кресле системного администратора (а общий стаж приближается уже годам к 15), столько вопросы офисной телефонии воспринимались мной строчкой из Californication калифорнийских же RHCP: hard core soft porn. Телефония всегда казалась параллельным измерением, в ее администрирование и настройку я старался не лезть. Точнее, вообще обходить все эти телефоновопросы по широкой дуге, скидывая все подобные задачи на «специально обученных людей».

Однако, как часто бывает, сколько веревочке не виться, а конец один… После трудоустройства в новой организации я стал «счастливым» администратором прекрасной связки из цифро-аналоговой АТС Ericsson и «маршрутизатора с интегрированными сервисами» Cisco 2911. Более сотни аналоговых телефонов, три десятка цифровых Ericsson'ов и два-три десятка IP-телефонов Cisco и Ericsson. Все это великолепие некогда было настроено теми самими спецами по hard core soft porn, и как оно работает, я и по сей день представляю лишь в общих чертах. Однако, как бы то ни было, что такое extension, как организовывается условная и безусловная переадресация, как устроен IVR, что такое DID и прочее, узнать мне все же пришлось. И я не был в восторге от обретенных знаний (да-да, не все полезное интересно и приятно). Лишь циска, после усвоения мною логики ее настройки, стала более или менее приятной железякой.

В какой-то момент пришло смирение, что телефония все же неотъемлемая часть хозяйства системного администратора.

И грянул гром


Организация, в которой я работаю, переезжает в другое здание. Разумеется, все аналоговое и цифровое хозяйство остается по месту вечной прописки. Цисковский VoIP в приготовлении 2911 обеспечивает 50 линий, да и ценник на телефонные аппараты циско совершенно негуманный. И то ли благодаря уже пришедшему смирению, то ли благодаря врожденной тяге к знанию, то ли благодаря сочетанию этих двух факторов я решил, что на новом месте телефонию я подниму сам.

Требования были простые: дешево, а лучше бесплатно, а, в идеале, чтобы нам доплачивали, обеспечить базовый функционал (прямые городские номера у сотрудников, IVR, переадресация, конференции, перевод вызова), от 150 линий и выше. Первое слово, пришедшее в голову, было «SIP», второе «Asterisk», третье «Гугл».

Был скачан и развернут AsteriskNOW. Ошалело потыкался в веб-интерфейсе. Оценил в который раз hardcornsoftporn'овскую логику телефонистов и снова отправился гуглить. И случайно натолкнулся на слово «FreeSWITCH». Новое слово, новые поисковые запросы. Хвалебные оды устойчивости, легкости настройки (всюду XML), нетребовательности к ресурсам, плюс работа «из коробки», плюс «у людей» без нареканий работает в виртуальном окружении с сотнями и даже тысячами линий без искажений голоса (хотя разработчики и не рекомендуют виртуальное окружение в связи с отсутствием доступа к CPU в реальном режиме времени). Ок. Пробуем.
Поднимаем виртуальную машину под рекомендованным Debian 8 x64, 1 ядро 2ГГц, 2 гига оперативки, 16 гиг диск. Выполняем установку в соответствии с инструкцией (тыц). Настраиваем софтфон (мне понравился 3CX'овский тыц).

Софтфон зарегистрировался. Тестовые звонки пошли. Ляпота. Ericsson Dialog 4422 тоже зарегистрировался, звонит, голос слышен в обе стороны. Ляпота. Аппарату Cisco 7945 была поставлена прививка прошивкой с поддержкой SIP, и, после относительно непродолжительных танцев с настройкой SCCP, аппарат так же зарегистрировался и звонки пошли без нареканий. Ляпота. FreeSWITCH в продакшн!

Хватит лирики


Исходные данные:

Оборудование коммутационной первого этажа:

  1. Коммутатор в собственности МТС, на котором нам выделена группа портов. С него мы забираем инет и будем забирать SIP-телефонию. Трафик тегирован vlan4
  2. Коммутатор наш, у него выделена группа портов, которым назначены vlan. В эти порты приходит инет от МТС (порт 2 — vlan2), инет от Miralogic (порт 3 — vlan3), SIP от МТС (порт 4 — vlan4). Так же выделен порт 1 под trunk, кабель уходит в серверную четвертого этажа.

Оборудование серверной на четвертом этаже:

  1. Коммутатор, принимающий trunk с первого этажа. Порт 1 — trunk входящий (с первого этажа. Порт 2 — trunk исходящий (к серверу).
  2. Сервер, на нем VMware vSphere 6. На этом хосте гостевая машина с Mikrotik RouterOS, выполняющая роль шлюза и файервола. Так же на этом хосте гостевая машина с FreeSWITCH. На хосте настроен vSwitch и добавлены порты с соответствующим тегированием трафика: vlan2, vlan3, vlan4.

Телефонию по E1 нам предоставляет МТС. От МТС же был предоставлен на тест SIP-транк.
Вот такие настройки были присланы мне инженерами МТС:

Добрый день!
Реквизиты SIP транка
Порт на коммутаторе 9
Номер 5555555
ip-адрес: 172.16.160.154
маска: 255.255.255.252
шлюз: 172.16.160.153
соединение без регистрации, точка - точка
sip-сервер: 172.16.253.3, port: 5060 UDP
media-сервер: 172.16.253.4, port: 10000 - 30000 UDP
кодеки: G711a/u
протокол передачи DTMF: rfc-2833

Машина с FreeSWITCH имеет один сетевой интерфейс, сетевые настройки:

IP: 172.18.253.1
Netmask: 255.255.0.0
GW: 172.18.1.254

Полетели, Mikrotik


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

Настраиваем.
Настройка интерфейса в Mikrotik
Выводим список имеющихся интерфейсов:
/interface ethernet print

Видим, что свежедобавленный интерфейс это интерфейс с именем ether6
Переименовываем его в if-sip:
/interface ethernet set [ find default-name=ether6 ] name=if-sip

Настраиваем адрес:
/ip address add address=172.16.160.154/30 comment=mts-sip interface=if-sip network=172.16.160.152

Настраиваем маршрутизацию в Mikrotik
Настраиваем mangle (чтобы трафик маркировался как мы захотим, и чтобы трафик ходил только через нужный нам интерфейс на основе этих меток):
/ip firewall mangle add action=mark-routing chain=prerouting comment="mark sip traffic" new-routing-mark=sip src-address=172.18.253.1

Настраиваем маршрутизацию:
/ip route add comment=mts-sip distance=1 dst-address=172.16.0.0/16 gateway=172.16.160.153 routing-mark=sip

Настраиваем файервол и NAT в Mikrotik
Настраиваем отсутствие фильтрации файерволом (на этом этапе я не боюсь злых хакеров из SIP-транка с PAN-адресов МТС):
/ip firewall filter
add chain=forward comment="allow any to mts-sip" dst-address=172.16.0.0/16 in-interface=if-local out-interface=if-sip src-address=172.18.253.1
add chain=forward comment="allow any from mts-sip" dst-address=172.18.253.1 in-interface=if-sip out-interface=if-local src-address=172.16.0.0/16


Убедитесь, что эти два разрешающих правила расположены в самом верху списка правил. Это снизит нагрузку на файервол.

Для себя я решил, что мой SIP-шлюз будет находиться в локальной сети, чтобы:

  • Не иметь трахомудии с нежеланием отдельных телефонных аппаратов работать со шлюзом, находящимся за NAT.
  • Внутренний трафик в десятки раз превышает исходящий и входящий вместе взятые,
    ни к чему его гонять через Mikrotik.
  • И вообще, настроить один раз шлюз будет дальновидней, чем в дальнейшем воевать с множеством телефонных аппаратов (сотни) с разными прошивками, а то и разных производителей.

По этой причине настраиваем 1:1 NAT.
/ip firewall nat
add action=netmap chain=srcnat comment="source NAT for sip" src-address=172.18.253.1 to-addresses=172.16.160.154
add action=netmap chain=dstnat comment="destination NAT for sip" dst-address=172.16.160.154 to-addresses=172.18.253.1

Если отключали сервисные порты, включаем sip обратно:
/ip firewall service-port set sip ports=5060,5061,5080,5081 disabled=no

Пингуем шлюз (172.16.160.153), пингуем SIP-шлюз (172.16.253.3), пингуем Media-шлюз (172.16.253.4). Пинги ходят. Прекрасно.

На этом легкая и понятная часть закончилась.

Полетели, FreeSWITCH


Для «выхода в свет» нам потребуется сделать следующие вещи, названия которых чужды уху айтишника, не сталкивавшегося с телефонией:
  • Указать, какие IP-адреса и где использовать FreeSWITCH'у для установления соединений.
  • Указать код региона и абонентский номер, выделенный оператором.
  • Создать extension — абонентский номер. Из коробки FreeSWITCH имеет преднастроенные 30 абонентских номеров. Мы создадим еще один.
  • Создать шлюз. Это не то, что вы подумали. Шлюз — это именованный набор настроек, необходимый для соединения с предоставленным SIP-транком. Мы же этого делать не будем,
    мы изменим настройки дефолтового шлюза
  • Настроить dialplan. В транслитерации ужасно звучащее слово. В правильном произношении звучащее еще хуже. Мне не нравится. В устоявшемся написании и произношении «диалплан». Означает набор сценариев. Из этого набора в зависимости от сработавшего условия FreeSWITCH выполняет то или иное действие. Диалпланы создаются для исходящих и входящих вызовов.
  • Настроить ACL для сетей и доменов, откуда разрешены звонки.

Disclaimer: предполагается, что FreeSWITCH установлен в папку по умолчанию: /etc/freeswitch

Самое первое, что делаем, меняем дефолтный пароль. Но опять не потому, почему вы подумали. Хотя и поэтому тоже. Пароль проверяется диалпланом, и если пароль совпадает с дефолтным, то перед соединением вставляется пауза 10 сек. Поэтому сначала меняем пароль.
Файл: /etc/freeswitch/vars.xml
<X-PRE-PROCESS cmd="set" data="default_password=ballsofsteel"/>

Как вариант, если вам так уж нравится пароль «1234», то можете убрать паузу из диалплана, просто отредактировав соответствующий блок.
Файл: /etc/freeswitch/dialplan/default.xml
<condition field="${default_password}" expression="^1234$" break="never">
  <action application="log" data="Fuck pretense just let's dance"/>
  <action application="log" data="Never Open $${conf_dir}/vars.xml and change default password."/>
</condition>

Настройки IP-адресов
Файл: /etc/freeswitch/sip_profiles/external.xml
<param name="ext-rtp-ip" value="172.16.160.154"/>
<param name="ext-sip-ip" value="172.16.160.154"/>

Региональные настройки
Файл: /etc/freeswitch/vars.xml
<X-PRE-PROCESS cmd="set" data="outbound_caller_name=3435555555"/>
<X-PRE-PROCESS cmd="set" data="outbound_caller_id=3435555555"/>
<X-PRE-PROCESS cmd="set" data="default_areacode=343"/>
<X-PRE-PROCESS cmd="set" data="default_country=RU"/>

Реквизиты от оператора
Файл: /etc/freeswitch/vars.xml
<X-PRE-PROCESS cmd="set" data="default_provider=172.16.253.3"/>
<X-PRE-PROCESS cmd="set" data="default_provider_username=3435555555"/>
<X-PRE-PROCESS cmd="set" data="default_provider_password=not-used"/>
<X-PRE-PROCESS cmd="set" data="default_provider_from_domain=172.16.253.3"/>
<X-PRE-PROCESS cmd="set" data="default_provider_register=true"/>
<X-PRE-PROCESS cmd="set" data="default_provider_contact=73435555555"/>

Ремарка самым внимательным
Вы, наверное, только что заметили, что значение параметра default_provider_register немного не вяжется с теми настройками, что выдал оператор (соединение без регистрации, точка — точка). Однако, в случае с МТС, регистрация все же понадобилась, без нее не проходили вызовы извне внутрь. При том, что без регистрации вполне можно было звонить изнутри наружу. Эта особенность выпила мне немало крови, прежде, чем я врубился и отловил ее.

Характерным признаком того, что требуется авторизация, является ошибка 407 Proxy Authentication Required.

Отлавливается эта ошибка включением SIP-трассировки:
sofia global siptrace on

Выключается трассировка, соответственно:
sofia global siptrace off

Добавляем абонентский номер (extension)
Создаем xml-файл с любым именем в папке /etc/freeswitch/directory/default. Я создал файл с именем, совпадающим с номером.
Файл: /etc/freeswitch/directory/default/4545.xml
<include>
 <user id="4545">
   <params>
    <param name="password" value="$${default_password}"/>
    <param name="vm-password" value="4545"/>
   </params>
   <variables>
    <variable name="toll_allow" value="domestic,international,local"/>
    <variable name="accountcode" value="4545"/>
    <variable name="user_context" value="default"/>
    <variable name="effective_caller_id_name" value="Скудицкий М.В."/>
    <variable name="effective_caller_id_number" value="4545"/>
    <variable name="outbound_caller_id_name" value="$${outbound_caller_name}"/>
    <variable name="outbound_caller_id_number" value="$${outbound_caller_id}"/>
    <variable name="callgroup" value="techsupport"/>
   </variables>
 </user>
</include>

Создаем шлюз
Скорее всего, после того, как вы выполнили настройки IP-адресов и заполнили поля в vars.xml реквизитами, выданными оператором, регистрация на шлюзе оператора состоится без проблем.

У меня же «без проблем» не получилось, поскольку МТС реквизиты выслал, а абонентский номер (extension) 3435555555 не завел. Прежде, чем я заподозрил оператора в подставе, я успел перебрать миллион вариантов настройки шлюза. Однако в итоге оператор у себя на SIP-шлюзе абонентский номер создал, и все завелось если и не с дефолтными настройками, то с близкими. Привожу листинг файла mts.xml, который был получен путем переименования файла external.xml.
Файл: /etc/freeswitch/directory/default/mts.xml
<include>
<user id="$${default_provider}">
  <gateways>
   <gateway name="$${default_provider}">
    <param name="username" value="$${default_provider_username}"/>
    <param name="password" value="$${default_provider_password}"/>
    <param name="from-user" value="$${default_provider_username}"/>
    <param name="from-domain" value="$${default_provider_from_domain}"/>
    <param name="expire-seconds" value="600"/>
    <param name="register" value="$${default_provider_register}">
    <param name="retry-seconds" value="30"/>
    <param name="extension" value="$${default_provider_contact}"/>
    <param name="context" value="public"/>
   </gateway>
  </gateways>
 <params>
  <param name="password" value="$${default_provider_password}"/>
 </params>
 </user>
</include>

Теперь добавляем разрешения на домен и на сети, чтобы наш FreeSWITCH не блокировал попытки установить соединения из домена и из сетей оператора. И да, на данном этапе я так же просто разрешаю все. Моя задача — позвонить уже наконец за пределы организации.
Файл: /etc/freeswitch/autoload_configs/acl.conf.xml
Так делать нельзя
<list name="lan" default="allow">
 <node type="allow" cidr="172.18.0.0/16"/>
 <node type="allow" cidr="172.16.0.0/16"/>
</list>
<list name="domains" default="allow">
 <node type="allow" domain="172.18.253.1"/>
 <node type="allow" domain="172.16.253.3"/>
 <node type="allow" cidr="172.16.0.0/16"/>
 <node type="allow" cidr="172.18.0.0/16"/>
</list>

<list name="lan" default="allow">
</list>
<list name="domains" default="deny">
 <node type="allow" cidr="172.16.253.3/32"/>  
</list>

Обратите внимание!
  1. ACL предназначен только для доменов и сетей провайдеров. Список domains должен быть по умолчанию deny. Использования переменной domain следует избегать, вместо нее следует использовать cidr и IP-адрес шлюза оператора с маской /32. Невыполнение этих базовых правил приводит к неожиданным последствиям в виде попыток заставить шлюз провайдера авторизоваться по пользователю и паролю при входящих звонках (Digest-авторизация), к попаданию внутренних абонентских номеров в неверный контекст (например, external, как следствие — переход к авторизации по ACL, вместо Digest) и нарушению работы диалпланов, etc.
  2. Это не файервол. Фильтрации трафика не происходит. Это списки доступа к сервисам FreeSWITCH'а.

Поздравляю! Можно посмотреть, что у нас (не) получилось.
Запускаем консоль FreeSWITCH'а:
fs_cli -rRS
Перезагружаем xml'ки:
reloadxml или жмакаем F6.
Перезагружаем ACL:
reloadacl
Перезагружаем модуль Sofia, отвечающий за телефонные соединения:
reload mod_sofia
Смотрим, что у нас с соединениями со шлюзами.
freeswitch@phone> sofia status
Name Type Data State
=================================================================================================
external profile sip:mod_sofia@172.16.160.154:5080 RUNNING (0)
external::172.16.253.3 gateway sip:3435555555@172.16.253.3 REGED
172.18.253.1 alias internal ALIASED
internal profile sip:mod_sofia@172.18.253.1:5060 RUNNING (0)
=================================================================================================
2 profiles 1 alias

Славно. Регистрация на шлюзе оператора прошла успешно.
Смотрим подробности:
sofia status gateway 172.16.253.3
freeswitch@phone> sofia status gateway 172.16.253.3
==============================================================
Name 172.16.253.3
Profile external
Scheme Digest
Realm 172.16.253.3
Username 3432788299
Password yes
From <sip:3435555555@172.16.253.3>
Contact <sip:gw+172.16.253.3@172.16.160.154:5080;transport=udp;gw=172.16.253.3>
Exten 73435555555
To sip:3435555555@172.16.253.3
Proxy sip:172.16.253.3
Context public
Expires 600
Freq 600
Ping 0
PingFreq 0
PingTime 0.00
PingState 0/0/0
State REGED
Status UP
Uptime 3932s
CallsIN 1
CallsOUT 1
FailedCallsIN 0
FailedCallsOUT 0
==============================================================

Дело встало за малым.

Диалпланы
Для звонков наружу создаем файл mts.xml с нуля, все остальные файлы в папке /etc/freeswitch/dialplan/default можно переименовать со сменой расширения/перенести в другое место/грохнуть.
Файл: /etc/freeswitch/dialplan/default/mts.xml
<include><extension name="outbound-city-call">
 <condition field="${toll_allow}" expression="local"/>
 <condition field="destination_number" expression="^(\d{7})$">
  <action application="set" data="effective_caller_id_number=${outbound_caller_id_number}"/>
  <action application="set" data="effective_caller_id_name=${outbound_caller_id_name}"/>
  <action application="bridge" data="sofia/gateway/${default_gateway}/8${default_areacode}$1"/>
 </condition>
</extension>
<extension name="outbound-country.cellular-call">
 <condition field="${toll_allow}" expression="domestic"/>
 <condition field="destination_number" expression="^8(\d{10})$">
  <action application="set" data="effective_caller_id_number=${outbound_caller_id_number}"/>
  <action application="set" data="effective_caller_id_name=${outbound_caller_id_name}"/>
  <action application="bridge" data="sofia/gateway/${default_gateway}/8$1"/>
 </condition>
</extension>
<extension name="outbound-international-call">
 <condition field="${toll_allow}" expression="international"/>
 <condition field="destination_number" expression="^(810\d+)$">
  <action application="set" data="effective_caller_id_number=${outbound_caller_id_number}"/>
  <action application="set" data="effective_caller_id_name=${outbound_caller_id_name}"/>
  <action application="bridge" data="sofia/gateway/${default_gateway}/810$1"/>
 </condition>
</extension>
</include>

Для звонков снаружи внутрь создаем файл mts_inbound.xml в папке /etc/freeswitch/dialplan/public. Остальное в этой папке по аналогии выше можно переименовать в *.bck или в *.noload
Файл: /etc/freeswitch/dialplan/public/mts_inbound.xml
<include>
 <extension name="public_did">
  <condition field="destination_number" expression="^(73435555555)$">
   <action application="set" data="domain_name=$${domain}"/>
   <action application="transfer" data="4545 XML default"/>    
   </condition>
  </extension>
</include>

Ну что. Скрестили пальцы, проверяем.

Снова запускаем консоль FreeSWITCH'а:
fs_cli -rRS
Перезагружаем xml'ки:
reloadxml или жмакаем F6
Перезагружаем модуль dialplan, отвечающий за телефонные соединения:
reload mod_dialplan_xml
Пробуем дозвон изнутри наружу. Пробуем дозвон снаружи внутрь. Радостно повторяем тестовое «раз-раз, два-два», убеждаемся, что звук ходит в обе стороны. Проверяем прохождение сигналов отбоя в обе стороны.

Радуемся, что приобщились к soft porn.

P.S.: Я сознательно избегал расшифровки значения тех или иных правок. Отчасти, потому что имена параметров и системных переменных очень уж хорошо говорят за себя. Отчасти, чтобы не превращать турбо-запуск телефонии в нудятину с оправданиями каждого пука.

P.P.S.: Написание сего текста было продиктовано ужасом от того, что я так и не смог найти внятный мануал слитным текстом, где решалась бы такая же задача: максимально просто, максимально быстро поднять телефонию с авторизацией точка-точка. Обилие документации почему-то не сопровождается обилием обсуждений и живых примеров. Или просто так получилось, что я допетрил до всего быстрее, чем нагуглил «тот самый пример», который бы сработал у меня. Так или иначе, пусть это будет шпаргалкой для меня и коллег на будущее.
Теги:
Хабы:
+14
Комментарии 28
Комментарии Комментарии 28

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн