load-net

KAMAILIO_MAN

Jun 21st, 2023
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 48.93 KB | None | 0 0
  1.  
  2.  
  3. Отличное приложение Asterisk, но свои косяки в нем тоже имеются, от
  4. утечек памяти появляющихся под большой нагрузкой, до багов которые еще
  5. никто не заметил. В итоге случается так, что до бесконечности Call
  6. центр на asterisk на одной машине масштабировать нельзя, рано или
  7. поздно утыкаемся в потолок производительности и система начинает
  8. периодически падать. Одна беда если просто звонок оборвался, но как
  9. правило Call центр у большой компании постепенно обрастает различным
  10. функционалом, по нему начинают вести статистику обращений по различным
  11. вопросам, по длительности вызовов на различные службы делают выводы о
  12. лояльности клиентов, по записанным разговорам решают конфликтные
  13. ситуации, по статистике очередей считают зарплаты операторам.
  14.  
  15. В общем: если компания завязана на общение с многочисленными клиентами, то
  16. нестабильная работа Call центра нарушает слаженное взаимодействие
  17. разных отделов, приводит к уменьшению эффективности работы компании и
  18. как следствие уменьшению прибыли. Это все конечно банальные вещи, но
  19. просто захотелось излить душу, потому что с данной проблемой я
  20. столкнулся в полной мере. Для специалиста по телефонии в большой
  21. компании с количеством абонентов > 100000 нестабильность Call центра
  22. приводит к появлению стойкой головной боли :) и появлению нервного тика
  23. :) , не говоря уже о том что даже банальный выезд за город происходит с
  24. задней мыслью, а что если Call центр опять свалится, а рядом меня не
  25. будет :)
  26.  
  27.  
  28. Как быть дальше?
  29.  
  30. Свелось все к тому, что так больше работать никто не мог. Решили
  31. принимать какие-то меры. Первая самая очевидная мера - это разделить
  32. Call центр на несколько машин. Да это работает, но не избавляет от всех
  33. прелестей по увеличению количества работы после каждой переконфигурации
  34. в системе, причем это количество работы растет с увеличением абонентов.
  35. Начали думать о каком-нибудь адски крутом проприетарном Call центре за
  36. много денег, надо сказать что я был категорически против, вспомнив
  37. только однажды увиденный прайс от Nec, с его кучей лицензий и
  38. непонятных "кабинетов" становилось плохо:). Слава ктулху мой голос был
  39. услышан и сомнительное решение за много денег покупать передумали, а
  40. решили попытаться построить решение на уже проверенном и знакомом
  41. Asterisk PBX.
  42.  
  43.  
  44. Что же мы хотим?
  45.  
  46. Чего хотелось бы от Call центра, чего на данный момент у нас не было?
  47.  
  48. 1. Безотказную систему, желательно с полным резервирование и без единой
  49. точки отказа
  50.  
  51. 2. Легкую реализацию load balansing между машинами Call центра
  52.  
  53. 3. Масштабируемую систему. Чтобы для обработки увеличившейся нагрузки
  54. необходимо было бы только добавить еще одну машину, причем чтобы это не
  55. требовало перенастройки всей системы, необходимо чтобы это мог сделать
  56. обычный инженер по эксплуатации (да у нас и такие есть :))
  57.  
  58. 4. Единую точку хранения учетных записей системы, у нас все сервисы для
  59. абонентов привязаны к логину и паролю, авторизацию для пользования
  60. сервисам осуществляет Radius, в общем - идеальный вариант, если Call
  61. центр будет работать с Radius
  62.  
  63. 5. Единая точка хранения статистики работы Call центра, ну тут и к
  64. бабке не ходи, asterisk-addons и mysql :)
  65.  
  66. 6. Гибкое API для разработки собственных интерфейсов управления и
  67. просмотра статистики работы Call центра и тут Asterisk нам подходит:
  68. AMI, CDR, queue_log
  69.  
  70. В общем необходимо было реализовать Load Balansing, Radius,
  71. масштабируемость и безотказность. Всего ничего :) , не говоря уже о том
  72. что в процессе разработки наверняка всплывет еще куча всяких ньюансов.
  73.  
  74. Как же все это сделать?
  75.  
  76. Краем уха я слышал где-то про SIP софтсвитч на OpenSer?, когда начал
  77. искать как же он работает наткнулся на одном из любимых форумов по
  78. asterisk на книженцию Building Telephony Systems with OpenSER.
  79. Благодаря ей был освоен очень нелегкий в понимании OpenSer?. Изучив
  80. документацию стало ясно, что авторизация через Radius это не проблема,
  81. балансировка тоже. Решено было строить систему такого вида:
  82.  
  83.  
  84.  
  85. Т.к. мы предоставляем сервис телефонии то имеем стык с PSTN с
  86. несколькими операторами по E1. В VoIP загоняем его через 2 Cisco
  87. AS5350. Которые гонят трафик по SIP на sipbalanser, в качестве которого
  88. используется софтсвитч kamailio, бывший проект OpenSer? (все наверное в
  89. курсе что этот проект форкнулся на kamailio и opensips, вроде оба
  90. проекта развиваются но документацию мне больше по душе у kamailio). На
  91. sipbalanser-е регистрируюся сотрудники Call центра, их авторизует
  92. Radius, kamailio для связи с Radius использует radiusclient-ng.
  93.  
  94. Логины и пароли хранятся в общей базе данных компании, в качестве сервера
  95. Radius используется Free Radius 2. Сведения о зарегистрированных
  96. клиентах хранятся в локальной базе данных на MySql?. Запросы на
  97. установление соединения маршрутизируются на sipbalanser, который затем
  98. с помощью модуля dispatcher распределяет их на ноды asterisk по
  99. алгоитму round robin. Asterisk обрабатывает звонок, если надо
  100. проигрывает музыку, помещает вызов в [112]IVR или делает любое
  101. свойственное asterisk-у действие с звонком, затем если нужно соединить
  102. с реальным человеком, то звонок отправляется на нужного нам сотрудника
  103. обратно на sipbalanser, тот ищет у себя в локальной базе
  104. зарегистрированного User Agent-а и если находит отдает ему вызов.
  105.  
  106.  
  107. Часть 2
  108.  
  109. В этой части я расскажу как настроить Kamailio чтобы его можно было
  110. использовать как SIP Proxy, на который будет возложена функции:
  111.  
  112. 1. Регистрации SIP User Agent-ов (авторизация возложена на Radius
  113. сервер)
  114.  
  115. 2. Преодоления NAT, с помощью media-proxy
  116.  
  117. 3. Маршрутизации вызовов
  118.  
  119. 4. Балансировки вызовов на ноды Asterisk
  120.  
  121.  
  122.  
  123. Как видно на схеме на sipbalanser-е запущены приложения:
  124.  
  125. 1. Kamailio - Наш SIP Proxy
  126.  
  127. 2. MySQL - в моем случае kamailio использует только одну таблицу из
  128. целой кучи таблиц разного назначения, я же использую только таблицу
  129. openser.locations, в ней храняться зарегистрированные UA, MySQL может
  130. находиться на отдельном сервере, у меня пока все на одном сервере
  131.  
  132. 3. Media-dispatcher - управляющий модуль mediaproxy, kamailio
  133. подключается к нему через UNIX сокет, служит для управления media-relay
  134. и вывода информации для мониторинга, написан на Python
  135.  
  136. 4. Media-relay - релей RTP трафика, написан на Python, но сам трафик
  137. релеит Linux ядро, (любителям FreeBSD не рекомендую использовать для
  138. релея RTP трафика предыдущие версии mediaproxy, они там работают, но
  139. при большом количестве звонков валится mediaproxy), используется
  140. механизм contrack, media-relay может находиться на отдельном сервер, и
  141. их может быть сколько угодно, очень удобно в плане масштабирования,
  142. сигнализацию на одном сервере обрабатываем, а RTP трафик на другом
  143.  
  144. 5. Radiusclient-ng - через него мы контактируем с radius
  145.  
  146. 6. Asterisk - :) о нем наверное сами все знаете
  147.  
  148.  
  149. Установка
  150.  
  151. Установка всего этого добра весьма банальна, все кроме Kamailio и
  152. mediaproxy можно поставить из портов, пакетов и репозитариев, как
  153. удобно, а ключевые для нас компоненты лучше ставить из исходников,
  154. чтобы и версии последние были и весь процесс прочувствовать и понять
  155. что для этих компонентов нужно. Я описывать процесс установки не буду,
  156. т.к. сам ставил давно, и уже не помню всех подробностей, а ставить
  157. заного ради статьи - лень J Все что нужно знать о установке подробно
  158. расписано в README. Единственное для mediaproxy почему-то нет init
  159. файлов, но это не беда, их легко написать самостоятельно.
  160.  
  161. Конфигурирование openser
  162.  
  163. Перейдем к основному, как настроить kamailio. Где-то я возможно
  164. повторюсь с статьей о openser с voip.rus.net, но там описан процесс
  165. настройки применительно к старым версиям Openser, в новых версиях
  166. kamailio многое изменилось и процесс настройки слегка изменился,
  167. особенно это касается работы c NAT.
  168. Конфигурационный файл я аттачить не буду, а буду приводить куски в
  169. статье, если эти куски собрать во едино, то получится нужный
  170. конфигурационный файл. Делаю это я осознанно, чтобы не возникало
  171. желания пролистать статью, ибо "много букв" J залить
  172. конфигурационный файл себе и получить все косяки из-за слабого
  173. представления что где зачем.
  174.  
  175. Я буду описывать не каждую строчку, т.к. предназначение некоторых
  176. банально, а некоторые я и сам не знаю зачем нужны, но в мануалах пишут
  177. что они нужны :)
  178.  
  179. Настоятельно рекомендую тем кто не знаком с процедурой установления
  180. соединения SIP протокола прочитать соответствующие мануалы. Не повредит
  181. знать структуру SIP пакетов т.к. kamailio для маршрутизации звонков
  182. использует те или иные поля SIP пакета.
  183.  
  184. Пожалуй начнем.
  185.  
  186. debug=3
  187. log_stderror=no
  188.  
  189.  
  190. отправлять или нет log сообщения в stdout, если стоит нет, есть
  191. возможность отправлять лог сообщения на syslog
  192.  
  193. fork=yes
  194.  
  195.  
  196. директива fork заставляет kamailio работать в режиме демона, иначе все
  197. сообщения будут попадать в stdout
  198.  
  199. children=8
  200. disable_dns_blacklist=yes
  201.  
  202.  
  203. disable_dns_blacklist=yes без этой опции kamailio может коряво работать
  204. с серверами на которые будем маршрутизировать сообщения
  205.  
  206. auto_aliases=no
  207. port=5060
  208. listen=udp:192.168.0.1:5060
  209.  
  210.  
  211. listen - понятен наверное для чего, параметров listen может быть
  212. несколько, но надо быть осторожным с ip маршрутами, чтобы сообщение
  213. поступив через один интерфейс не уходило обратно через другой, если
  214. конечно именно не это требуется :)
  215.  
  216. alias=voip.telecom.ru:5060
  217. alias= sip.telecom.ru:5060
  218. alias=192.168.0.1:5060
  219.  
  220.  
  221. список alias-ов, синонимом которых является сервер
  222.  
  223. mpath="/usr/local/lib/kamailio/modules/"
  224.  
  225.  
  226. Путь до папки с модулями kamailio
  227.  
  228. loadmodule "pv.so"
  229. loadmodule "db_mysql.so"
  230. modparam("db_mysql", "auto_reconnect", 1)
  231.  
  232.  
  233. загружаем модуль работы с mysql и включаем авто реконнект.
  234.  
  235. loadmodule "sl.so"
  236. loadmodule "tm.so"
  237. modparam("tm", "fr_timer", 10)
  238. modparam("tm", "fr_inv_timer", 120)
  239. modparam("tm", "wt_timer", 5)
  240. modparam("tm", "delete_timer", 2)
  241. modparam("tm", "ruri_matching", 1)
  242. modparam("tm", "via1_matching", 1)
  243. modparam("tm", "unix_tx_timeout", 2)
  244. modparam("tm", "restart_fr_on_each_reply", 1)
  245. modparam("tm", "pass_provisional_replies", 0)
  246. loadmodule "rr.so"
  247. modparam("rr", "enable_full_lr", 1)
  248. modparam("rr", "append_fromtag", 0)
  249. loadmodule "maxfwd.so"
  250. modparam("maxfwd", "max_limit", 256)
  251. loadmodule "usrloc.so"
  252. modparam("usrloc", "db_mode", 1)
  253. modparam("usrloc", "timer_interval", 120)
  254. modparam("usrloc", "db_url", "mysql://openser:openserrw@localhost/openser")
  255. modparam("usrloc", "nat_bflag", 6)
  256. modparam("usrloc", "expires_column", "expires")
  257.  
  258.  
  259. задаем параметры базы данных, куда конектится, какую базу использовать,
  260. выбираем для флага сигнализирующего принадлежность клиента к клиентам
  261. за натом bflag 6, подробнее о флагах можно почитать на сайте kamailio
  262.  
  263. loadmodule "registrar.so"
  264. modparam("registrar", "method_filtering", 1)
  265. modparam("registrar", "max_expires", 50)
  266. modparam("registrar", "default_q", 0)
  267. modparam("registrar", "append_branches", 1)
  268. modparam("registrar", "case_sensitive", 0)
  269. modparam("registrar", "max_contacts", 0)
  270. modparam("registrar", "retry_after", 0)
  271. modparam("registrar", "method_filtering", 0)
  272.  
  273.  
  274. модуль отвечающий за обработку сообщений Register, у меня выставлен
  275. таймер максимального времени перерегистрации в 50 секунд, это связано с
  276. тем что мы используем у себя переделанный клиент QuteCom?, который если
  277. регается реже начинает терять регистрацию и валится в корку.
  278. max_expires заставляет всех клиентов регаться не реже чем раз в 50
  279. секунд.
  280.  
  281. loadmodule "textops.so"
  282. loadmodule "xlog.so"
  283. loadmodule "mi_fifo.so"
  284. modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
  285. modparam("mi_fifo", "fifo_mode", 0660)
  286. modparam("mi_fifo", "fifo_group", "openser")
  287. modparam("mi_fifo", "fifo_user", "openser")
  288. modparam("mi_fifo", "reply_dir", "/tmp/")
  289. modparam("mi_fifo", "reply_indent", "\t")
  290.  
  291.  
  292. /tmp/kamailio_fifo - это сокет для управления и мониторинга kamailio,
  293. управляет и мониторит приложении kamctl
  294.  
  295. loadmodule "uri_db.so"
  296. modparam("uri_db", "use_uri_table", 0)
  297. modparam("uri_db", "db_url", "")
  298. loadmodule "siputils.so"
  299. loadmodule "nathelper.so"
  300. modparam("nathelper", "rtpproxy_disable", 1)
  301. modparam("nathelper", "natping_interval", 10)
  302. modparam("nathelper", "received_avp", "$avp(i:42)")
  303.  
  304.  
  305. Т.к. будем использовать mediaproxy, то выключаем поддержку rtpproxy,
  306. задаем интервал <<пингания>> UA, чтобы роутеры держали открытыми порты.
  307.  
  308. loadmodule "avpops.so"
  309. loadmodule "auth.so"
  310. loadmodule "auth_db.so"
  311. loadmodule "dispatcher.so"
  312. modparam("dispatcher", "flags", 2 )
  313. modparam("dispatcher", "list_file", "/usr/local/etc/kamailio/dispatcher.list")
  314. modparam("dispatcher", "dst_avp", "$avp(i:271)")
  315. modparam("dispatcher", "grp_avp", "$avp(i:272)")
  316. modparam("dispatcher", "cnt_avp", "$avp(i:273)")
  317.  
  318.  
  319. модуль dispatcher служит для реализации load balancing-а, в list file
  320. хранятся адреса различных серверов на которые мы будем распределять
  321. вызовы.
  322.  
  323. loadmodule "auth_radius.so"
  324. modparam("auth_radius", "radius_config","/etc/radiusclient-ng/radiusclient.conf")
  325. modparam("auth_radius", "service_type",1)
  326. modparam("auth_radius", "use_ruri_flag", 22)
  327.  
  328.  
  329. включаем поддержку radius, указываем путь до конфига радиус сервера
  330.  
  331. loadmodule "mediaproxy.so"
  332. modparam("mediaproxy","mediaproxy_socket", "/var/run/mediaproxy/dispatcher.sock")
  333.  
  334.  
  335. включаем работу с mediaproxy, задаем путь до сокета media-dispatcher
  336.  
  337. loadmodule "domain.so"
  338. modparam("domain", "db_url", "mysql://openser:openserrw@localhost/openser")
  339. modparam("domain", "db_mode", 1)
  340. modparam("domain", "domain_table", "domain")
  341. modparam("domain", "domain_col", "domain")
  342. loadmodule "presence.so"
  343. modparam("presence", "db_url", "mysql://openser:openserrw@localhost/openser")
  344. modparam("presence", "max_expires", 3600)
  345. modparam("presence", "server_address", "sip:sippalanser.is74.ru:5060")
  346. loadmodule "dialog.so"
  347. modparam("dialog", "dlg_flag", 4)
  348. loadmodule "nat_traversal.so"
  349. modparam("nat_traversal", "keepalive_interval", 90)
  350. modparam("nat_traversal", "keepalive_method", "OPTIONS")
  351.  
  352.  
  353. Kamailio читает конфигурационный файл от начала до конца,
  354. соответственно диалплан исполняется как обычная программа. Существует
  355. несколько разновидностей блоков маршрутизации, которые зовутся: route -
  356. основной блок маршрутизации, route[x] - что-то типа процедур, только
  357. без параметров, все параметры передаются с помощью флагов и переменных,
  358. t_on_reply - блок для обработки различных ответов, failure_route - для
  359. обработки ошибок
  360.  
  361. route
  362. {
  363.  
  364.  
  365. Основной блок маршрутизации, по аналогии с С/C++ main() {}
  366.  
  367. if (method=="OPTIONS")
  368. {
  369. exit;
  370. };
  371. if (method=="PUBLISH")
  372. {
  373. exit;
  374. };
  375. if (method=="SUBSCRIBE")
  376. {
  377. exit;
  378. };
  379.  
  380.  
  381. Я не использую у себя эти сообщения пока, чтобы не мешались оправляю их
  382. в dev/null :)
  383.  
  384. if (!mf_process_maxfwd_header("10"))
  385. {
  386. sl_send_reply("483","Too Many Hops");
  387. exit;
  388. };
  389. if (msg:len > max_len )
  390. {
  391. sl_send_reply("513", "Message Overflow");
  392. exit;
  393. };
  394.  
  395.  
  396. Небольшая защита от больших пакетов и зацикленных вызовов.
  397.  
  398. # -----------------------------------------------------------------
  399. # Record Route Section
  400. # -----------------------------------------------------------------
  401. if (method=="INVITE" && nat_uac_test("2"))
  402. {
  403. xlog("L_INFO", "record route section | INVITE & nat test: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  404. record_route_preset("192.168.0.1:5060;nat=yes");
  405. }
  406.  
  407.  
  408. Если получено сообшение INVITE и процедура проверки ната вернула нам
  409. истину, то явно указываем заголовочное поле Record-Route. (nat_uac_test
  410. может по разному определять ваш нат, для этого у него есть несколько
  411. методов определения ната, советую почитать документацию и опытно
  412. теоретическим путем выяснить какой аргумент этой функции вам подойдет).
  413.  
  414. Функция xlog отсылает указанное сообщение в аргументе на syslog или на
  415. stdout
  416.  
  417. else if (method!="REGISTER")
  418. {
  419. record_route();
  420. }
  421.  
  422.  
  423. Если от SIP клиента, находящегося за маршрутизатором с NAT, получено
  424. сообщение не INVITE и не REGISTER типа, то только тогда мы вызываем
  425. функцию record_route(), для гарантии того, чтобы сообщения проходили
  426. через наш SIP прокси сервер с вышестоящих и нижестоящих SIP прокси
  427. серверов или со шлюзов в публичную телефонную сеть (PSTN).
  428.  
  429. if (method=="BYE" || method=="CANCEL")
  430. {
  431. xlog("L_INFO", "Call Tear Down | end_media_session: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  432. end_media_session();
  433. };
  434.  
  435.  
  436. Отмена или конец вызова, вызывается end_media_session, это фунцкия
  437. модуля mediaproxy, даже если у нас не был mediaproxy задействован для
  438. установления вызова совершенно безопасно на всякий случай вызывать эту
  439. функцию чтобы наверняка разорвать соединение.
  440.  
  441. # -----------------------------------------------------------------
  442. # Loose Route Section
  443. #Секция свободной маршрутизации
  444. # -----------------------------------------------------------------
  445.  
  446.  
  447. Эта секция включается когда сообщение адресовано не нашему серверу, а
  448. проходит через него транзитом
  449.  
  450. if (loose_route())
  451. {
  452. xlog("L_INFO", "loose route\n");
  453. if ((method=="INVITE" || method=="REFER") && !has_totag())
  454. {
  455. sl_send_reply("403", "Forbidden");
  456. return;
  457. };
  458.  
  459.  
  460. Мы должны особым образом обрабатывать сообщения Re-Invite, дабы
  461. предотвратить разрыв потоков по RTP протоколу, во время обработки этих
  462. сообщений. Таким образом, в этом месте мы отдельно обрабатываем эти
  463. сообщения для клиентов, находящихся за NAT.
  464.  
  465. Для гарантии того, что мы получили действительно повторное сообщение
  466. INVITE (re-INVITE), мы должны убедиться, что функция has_totag() и
  467. loose_route() вернула TRUE. Причина в том, что возможно оригинальное
  468. сообщение INVITE содержит предопределенные заголовочные поля для
  469. маршрутов, что заставило бы loose_route() вернуть TRUE. Поэтому
  470. производим проверку функцией has_totag(), т.к. только для уже
  471. установленных соединений будет содержаться флаг "tag=" в заголовочном
  472. поле <To> (т.е., только для вызовов, где, вызываемым абонентом, был
  473. подтвержден запрос на установку соединения сообщением с кодом "200
  474. OK").
  475.  
  476. Другими словами, эта новая проверка безопасности основывается на том
  477. факте, что уже установленные SIP вызовы будут содержать "totag", тогда
  478. как еще не установленные - его не содержат. Для гарантии того, что наша
  479. логика "свободной маршрутизации" не будет использована в злонамеренных
  480. целях, мы проверяем тот факт, что эти сообщения INVITE и REFER, приняты
  481. в рамках уже установленного соединения.
  482.  
  483. if (method=="INVITE")
  484. {
  485. if (nat_uac_test("2") || search("^Route:.*;nat=yes"))
  486. {
  487.  
  488.  
  489. Теперь мы проверяем NAT статус отправителя сообщения re-INVITE, вызывая
  490. функцию nat_uac_test("3"). Также ищем заголовочное поле <Route>,
  491. содержащий тег ";nat=yes", который будет вставлен ранее, обсуждаемой
  492. ранее, функцией record_route_preset(). Если найден тэг ";nat=yes",
  493. тогда вызывающий абонент находиться за маршрутизатором с NAT.
  494.  
  495. setbflag(6);
  496.  
  497.  
  498. Если отправитель сообщения находиться за маршрутизатором с NAT или
  499. INVITE сообщение содержит флаг "nat=yes", тогда мы устанавливаем флаг 6
  500. для использование его в дальнейшем.
  501.  
  502. use_media_proxy();
  503.  
  504.  
  505. Для начала проксирования RTP потоков, мы вызываем функцию
  506. use_media_proxy(). Она будет общаться с внешним mediaproxy сервером,
  507. заставляя это открыть UDP порты для обоих клиентов, или управлять
  508. существующим сеансом RTP проксирования для уже установленного вызова, в
  509. зависимости от заголовочного поля <Call-ID>. Вызов use_media_proxy()
  510. вызывает перезапись содержимого SDP, в части IP адреса и портов,
  511. которые выделил mediaproxy сервер для RTP потоков
  512.  
  513. }
  514. else
  515. {
  516. xlog("L_INFO", "loose route | Re-INVITE from NO NAT: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  517. route(4);
  518. }
  519. }
  520. route(1);
  521. exit;
  522. }
  523.  
  524.  
  525. Если NAT -а нет, то просто отправляем звонок на route[4] для
  526. проверки находится ли абонент за NAT и на route[1] для доставки
  527. сообщения по назначению, что будет происходить там, мы выясним чуть
  528. позже.
  529.  
  530. # -----------------------------------------------------------------
  531. # Call Type Processing Section
  532. # Секция, обрабатывающая различные типы вызовов.
  533. # -----------------------------------------------------------------
  534. if (uri!=myself)
  535. {
  536. xlog("L_INFO", "MESSAGE NOT MYSELF: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  537. route(4);
  538. route(1);
  539. exit;
  540. };
  541.  
  542.  
  543. Если сообщение больше не нуждается в обработке нашим SIP серверов, то
  544. отправляем его на route[4] route[1]
  545.  
  546. if (method=="ACK")
  547. {
  548. xlog("L_INFO", "route ACK detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  549. route(1);
  550. exit;
  551. }
  552.  
  553.  
  554. Получено сообщение ACK, отдельное правило создано для нужд отладки,
  555. такие сообщение мы должны сразу на route[1] отдать.
  556.  
  557. else if (method=="CANCEL")
  558. {
  559. xlog("L_INFO", "route CANCEL detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  560. end_media_session();
  561. route(1);
  562. exit;
  563. }
  564.  
  565.  
  566. Получено команда отмены вызова, вызываем end_media_session, чтобы
  567. наверняка закрыть UDP порты на mediaproxy, даже если UA не за NAT и
  568. mediaproxy не использовался, совершенно безопасно вызвать эту команду и
  569. отправляем Cancel по назначению чтобы другой UA тоже закончил процедуру
  570. установления сессии.
  571.  
  572. else if (method=="INVITE")
  573. {
  574. route(3);
  575. exit;
  576. }
  577.  
  578.  
  579. Если получено сообщение INVITE то отдаем его сразу на route[3],
  580. там содержится вся логика установления соединения.
  581.  
  582. else if (method=="REGISTER")
  583. {
  584. xlog("L_INFO", "route REGISTER detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si $mb\n");
  585. route(2);
  586. exit;
  587. }
  588.  
  589.  
  590. Если получено сообщение REGISTER то будем его обрабатывать в route[2]
  591.  
  592. route(1);
  593.  
  594.  
  595. все остальные сообщения отправляем по назначению.
  596.  
  597. # -----------------------------------------------------------------
  598. # Default Message Handler
  599. # -----------------------------------------------------------------
  600. route[1]
  601. {
  602. xlog("L_INFO", "route[1] default handler: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  603. t_on_reply("1");
  604.  
  605.  
  606. При работе с UA находящимися за NAT мы должны корректно обрабатывать
  607. сообщения возвращающиеся к UA, к этим сообщениям можно получить доступ
  608. через блок reply_route
  609.  
  610. if (!t_relay())
  611.  
  612.  
  613. вызываем функцию t_relay, это statefull функция, т.е. с сохранением
  614. состояния транзакции. Т.е. если после начала транзакции Invite
  615. сообщением отправить ACK или BYE то это сообщение будет отправлено
  616. именно тому UA который это сообщение ждет.
  617.  
  618. {
  619. xlog("L_INFO", "route[1] | ERROR: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  620. if (method=="INVITE" || method=="ACK")
  621. {
  622. xlog("L_INFO", "route[1] | INVITE or ACK error: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  623. xlog("L_INFO", "route[1] | end_media_session: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  624. end_media_session();
  625. }
  626. sl_reply_error();
  627.  
  628.  
  629. Ошибка доставки сообщения
  630.  
  631. }
  632. }
  633. # ------------------------------------------------------------------------
  634. # Обработка REGISTER
  635. # ------------------------------------------------------------------------
  636. route[2]
  637. {
  638. sl_send_reply("100", "Trying");
  639. xlog("L_INFO", "route[2] REGISTER Message Handler M=$rm RURI=$ru F=$fu T=$tu IP=$si");
  640. if (!search("^Contact:[ ]*\*") && nat_uac_test("2"))
  641. {
  642. setbflag(6);
  643. fix_nated_register();
  644.  
  645.  
  646. Fix_nated_register() специально используется для обработки сообщений
  647. REGISTER от клиентов, находящихся за NAT
  648.  
  649. force_rport();
  650.  
  651.  
  652. функция Force_rport () добавляет полученный IP порт в самое начало
  653. заголовочных полей "via" SIP сообщения. Это дает возможность направлять
  654. последующие SIP сообщения на нужный порт для последующих SIP
  655. транзакций.
  656.  
  657. fix_contact();
  658.  
  659.  
  660. переписываем IP и порт в заловке Contact, чтобы в таблице
  661. зарегистрированных UA был не локальный адрес за натом, а адрес ната и
  662. порт через который можно достучаться до UA
  663.  
  664. }
  665.  
  666.  
  667. Проверяем UA от которого пришло сообщение, за NAT он или нет, если
  668. проверка истина то выставляем bflag 6, этот флаг будет сохранен в
  669. таблице location, kamailio всегда будет знать какой UA за Nat или нет,
  670. чтобы иметь возможность задействовать mediaproxy для установления RTP
  671. сессии
  672.  
  673. if(is_method("REGISTER") && is_present_hf("Expires") && $(hdr(Expires){s.int})==0)
  674. {
  675. xlog("L_INFO", "UNREGISTER: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  676. }
  677.  
  678.  
  679. Если поле Expires=0 значит UA отрегивается, не будем уточнять его
  680. параметры авторизации.
  681.  
  682. else
  683. {
  684. if(!radius_www_authorize(""))
  685. {
  686. xlog("L_INFO", "radius_www_authorize() error M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  687. www_challenge("","1");
  688. exit;
  689. }
  690. else
  691. {
  692. if (!check_to())
  693. {
  694. sl_send_reply("401", "Unauthorized");
  695. exit;
  696. }
  697. consume_credentials();
  698. }
  699. };
  700.  
  701.  
  702. В противном случае авторизуем его. У нас авторизация реализована через
  703. Radius, только не спрашивайте как настраивать Radius сервер какие
  704. атрибуты надо править. У нас отдельный человек занимается Radius-ом, и
  705. настройка всего этого его рук дело. Наш Radius сервер претерпел
  706. множественные изменения исходников для нужд нашей компании, поэтому его
  707. конфиги вам совершенно не будут интересны. В книге про openser есть
  708. пример как использовать аккаунты в базе для регистрации, на сайте есть
  709. туториал по работе с radius в нете полно примеров разных конфигураций,
  710. так что в этом у вас есть полная свобода.
  711.  
  712. if (!save("location"))
  713. {
  714. sl_reply_error();
  715. }
  716. }
  717.  
  718.  
  719. Сохраняем информацию о UA в базе kamailio, в таблице locations.
  720.  
  721. Секция обработки INVITE
  722.  
  723. route[3]
  724. {
  725. xlog("L_INFO", "route[3] invite handler: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  726. if (nat_uac_test("2"))
  727. {
  728. setbflag(7);
  729.  
  730.  
  731. здесь мы выставляем bflag 7, т.е. когда исходящий вызов сигнализатором
  732. NAT-а будет флаг 7, а при входящем вызове на абонента за NAT-ом флаг 6
  733.  
  734. force_rport();
  735. fix_contact();
  736. }
  737.  
  738.  
  739. Если INVITE от клиента за NAT делаем с ним то же что сделали до этого в
  740. route[2] Здесь было бы неплохо сделать авторизацию INVITE, дабы
  741. кто попало не позвонил и не поговорил на халяву, если у вас конечно
  742. этот сервер будет обслуживать клиентов, которым надо насчитать денег.
  743. Но на практике kamailio не до конца справляется с авторизацией INVITE.
  744. 20-30 % звонков завершаются аварийно, т.к. kamailio решает что
  745. авторизация не пройдена. Товарищ tma c форума asterisk-support.ru пишет
  746. Проблема в том, что INVITE может придти без необходимых для Digest
  747. авторизации данных, в результате биллинг/radius "отшивает" запрос, а
  748. Kamailio/SER/OpenSER, как следствие, его рвет."
  749.  
  750. На практике авторизация работает примерно так, что для REGISTER что для INVITE
  751. запросов. Приходит первоначальный запрос, если в нем нет необходимой
  752. Digest информации для авторизации запроса, то сервер должен вернуть
  753. false, потом он отправляет 401 Unauthorized, на что клиент должен
  754. попробовать еще раз отправить этот запрос но уже с необходимыми Digest
  755. данными, для запросов REGISTER это прокатывает, а вот для INVITE-ов
  756. почему-то не всегда, сложный вопрос почему это так, я не знаю причин и
  757. пока решил вызовы не проверять, есть идеи как такую проверку
  758. реализовать, но это уже отдельная тема
  759.  
  760. Lookup("location") заставляет сервер проверить есть ли UA которому
  761. хочет позвонить клиент отправивший INVITE в списке зареганных, если он
  762. есть то функция возвращает его реквизиты, а также значение bflag 6,
  763. установлен или нет
  764.  
  765. if (!lookup("location"))
  766. {
  767.  
  768.  
  769. UA не найден, значит либо он не зареган либо звонят не UA находящемуся
  770. на этом сервере
  771.  
  772. xlog("L_INFO", "route[3] | not local client: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  773. route(5);
  774. exit;
  775.  
  776.  
  777. если нужного номера нет на этом сервере, то поищем его на asterisk
  778. сервере, route[5] отвечает за перенаправление вызова на asterisk
  779.  
  780. }
  781. else
  782. {
  783. xlog("L_INFO", "route[3] | local client: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  784. }
  785.  
  786.  
  787. клиент найден на сервер, отправляем его на route[4], который в
  788. случае необходимости включит mediaproxy и собственно по назначению на
  789. route[1]
  790.  
  791. route(4);
  792. route(1);
  793. }
  794.  
  795.  
  796. Включаем mediaproxy
  797.  
  798. route[4]
  799. {
  800. #xlog("L_INFO", "route[4] nat traversal: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  801. if (isbflagset(6) || isbflagset(7))
  802. {
  803.  
  804.  
  805. Проверяем, если установлен bflag 6, то вызов на UA который за NAT, если
  806. установлен bflag 7, то вызов от UA который за NAT
  807.  
  808. if (!isbflagset(8))
  809. {
  810. setbflag(8);
  811. xlog("L_INFO", "route[4] | use_media_proxy: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  812. use_media_proxy();
  813. }
  814.  
  815.  
  816. Включаем mediaproxy, простенькая защита от многократного включения
  817. mediaproxy для одного вызова, реализована посредством конструкции c
  818. bflag 8
  819.  
  820. }
  821. }
  822.  
  823.  
  824. Вот собственно секция для выбора нужной ноды asterisk, работает модуль
  825. dispatcher
  826.  
  827. route[5]
  828. {
  829. t_on_reply("2");
  830. t_on_failure("1");
  831.  
  832.  
  833. обработка ошибок и сообщений
  834.  
  835. xlog("L_INFO", "route[5]->asterisk node: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  836. ds_select_domain("1","4");
  837.  
  838.  
  839. выбираем группу серверов 1 и механизм распределения вызовов 4 - round
  840. robin
  841.  
  842. route(4);
  843. route(1);
  844.  
  845.  
  846. отправляем вызов куда следует
  847.  
  848. }
  849.  
  850.  
  851. В файле dispatcher.lis который мы указали здесь modparam("dispatcher",
  852. "list_file", "/usr/local/etc/kamailio/dispatcher.list") мы прописываем
  853. необходимые сервера куда будем балансировать нагрузку.
  854.  
  855. 1 sip:192.168.0.1:5060
  856. 1 sip:192.168.0.2:5060
  857.  
  858.  
  859. Вот на эти два сервера и пойдут запросы
  860.  
  861. 2 sip:192.168.0.3:5060
  862. 2 sip:192.168.0.4:5060
  863.  
  864.  
  865. А сюда бы они пошли если бы мы указали в конфиге kamailio
  866.  
  867. ds_select_domain("2","4");
  868.  
  869.  
  870. Обработка ошибок и сообщений.
  871.  
  872. onreply_route[1]
  873. {
  874. if ((isbflagset(6) || isbflagset(7)) && (status=~"(180)|(183)|2[0-9][0-9]")
  875. )
  876. {
  877. if (!search("^Content-Length:[ ]*0"))
  878. {
  879. use_media_proxy();
  880. };
  881. if (nat_uac_test("2"))
  882. {
  883. fix_contact();
  884. };
  885. };
  886. }
  887. onreply_route[2]
  888. {
  889. if (status=~"[12][0-9][0-9]")
  890. {
  891. fix_nated_contact();
  892. exit;
  893. };
  894. }
  895. failure_route[1]
  896. {
  897. if( t_check_status("408") )
  898. {
  899. xlog( "L_NOTICE", "[$Tf] FR: $ci -- TIMEOUT for Gateway $rd\n" );
  900. }
  901. else
  902. {
  903. xlog( "L_NOTICE", "[$Tf] FR: $ci -- $rs reason $rr\n" );
  904. };
  905. if( t_check_status("403") )
  906. {
  907. xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Forbidden -> ISDN Cause Code1\n" );
  908. return;
  909. };
  910. if( t_check_status("486") )
  911. {
  912. xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Destination BUSY \n" );
  913. return;
  914. };
  915. if( t_check_status("487") )
  916. {
  917. xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Request Cancelled\n" );
  918. return;
  919. };
  920. if( ds_next_domain() )
  921. {
  922. t_on_reply("2");
  923. xlog( "L_NOTICE", "[$Tf] FR: $ci Next gateway $fU -> $tU via $rd\n" );
  924. if( !t_relay() )
  925. {
  926. xlog( "L_INFO", "[$Tf] FR: $ci -- ERROR - Can not t_relay()\n" );
  927. return;
  928. };
  929. return;
  930. }
  931. else
  932. {
  933. xlog( "L_INFO", "[$Tf] FR: $ci No more buscuits in the gateways" );
  934. t_reply("503", "Service unavailable -- no more gateways" );
  935. exit;
  936. };
  937. xlog("L_INFO", "failure_route[1]->: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
  938. }
  939.  
  940.  
Add Comment
Please, Sign In to add comment