Немного лирики
Сколько помню себя в кресле системного администратора (а общий стаж приближается уже годам к 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 в продакшн!
Хватит лирики
Исходные данные:
Оборудование коммутационной первого этажа:
- Коммутатор в собственности МТС, на котором нам выделена группа портов. С него мы забираем инет и будем забирать SIP-телефонию. Трафик тегирован vlan4
- Коммутатор наш, у него выделена группа портов, которым назначены vlan. В эти порты приходит инет от МТС (порт 2 — vlan2), инет от Miralogic (порт 3 — vlan3), SIP от МТС (порт 4 — vlan4). Так же выделен порт 1 под trunk, кабель уходит в серверную четвертого этажа.
Оборудование серверной на четвертом этаже:
- Коммутатор, принимающий trunk с первого этажа. Порт 1 — trunk входящий (с первого этажа. Порт 2 — trunk исходящий (к серверу).
- Сервер, на нем 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
Выводим список имеющихся интерфейсов:
Видим, что свежедобавленный интерфейс это интерфейс с именем ether6.
Переименовываем его в if-sip:
Настраиваем адрес:
Настраиваем маршрутизацию в Mikrotik
Настраиваем mangle (чтобы трафик маркировался как мы захотим, и чтобы трафик ходил только через нужный нам интерфейс на основе этих меток):
Настраиваем маршрутизацию:
Настраиваем файервол и NAT в Mikrotik
Настраиваем отсутствие фильтрации файерволом (на этом этапе я не боюсь злых хакеров из SIP-транка с PAN-адресов МТС):
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.
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 обратно:
Пингуем шлюз (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-трассировки:
Выключается трассировка, соответственно:
Добавляем абонентский номер (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>
Обратите внимание! Это не файервол. Фильтрации трафика не происходит. Это списки доступа к сервисам FreeSWITCH'а.
Поздравляю! Можно посмотреть, что у нас (не) получилось.
Запускаем консоль FreeSWITCH'а:
fs_cli -rRS
Перезагружаем xml'ки:
reloadxml или жмакаем F6
Перезагружаем модуль 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.: Написание сего текста было продиктовано ужасом от того, что я так и не смог найти внятный мануал слитным текстом, где решалась бы такая же задача: максимально просто, максимально быстро поднять телефонию с авторизацией точка-точка. Обилие документации почему-то не сопровождается обилием обсуждений и живых примеров. Или просто так получилось, что я допетрил до всего быстрее, чем нагуглил «тот самый пример», который бы сработал у меня. Так или иначе, пусть это будет шпаргалкой для меня и коллег на будущее.