Asterisk Load Balansing Часть 2

Skip to end of metadata
Go to start of metadata

В этой части я расскажу как настроить Kamailio чтобы его можно было использовать как SIP Proxy, на который будет возложена функции:

  1. Регистрации SIP User Agent-ов (авторизация возложена на Radius сервер)
  2. Преодоления NAT, с помощью media-proxy
  3. Маршрутизации вызовов
  4. Балансировки вызовов на ноды Asterisk

Как видно на схеме на sipbalanser-е запущены приложения:

  1. Kamailio – Наш SIP Proxy
  2. [MySQL] – в моем случае kamailio использует только одну таблицу из целой кучи таблиц разного назначения, я же использую только таблицу openser.locations, в ней храняться зарегистрированные UA, [MySQL] может находиться на отдельном сервере, у меня пока все на одном сервере
  3. Media-dispatcher – управляющий модуль mediaproxy, kamailio подключается к нему через UNIX сокет, служит для управления media-relay и вывода информации для мониторинга, написан на Python
  4. Media-relay – релей RTP трафика, написан на Python, но сам трафик релеит Linux ядро, (любителям [FreeBSD] не рекомендую использовать для релея RTP трафика предыдущие версии mediaproxy, они там работают, но при большом количестве звонков валится mediaproxy), используется механизм contrack, media-relay может находиться на отдельном сервер, и их может быть сколько угодно, очень удобно в плане масштабирования, сигнализацию на одном сервере обрабатываем, а RTP трафик на другом
  1. Radiusclient-ng – через него мы контактируем с radius
  2. Asterisk -  о нем наверное сами все знаете

Установка
Установка всего этого добра весьма банальна, все кроме Kamailio и mediaproxy можно поставить из портов, пакетов и репозитариев, как удобно, а ключевые для нас компоненты лучше ставить из исходников, чтобы и версии последние были и весь процесс прочувствовать и понять что для этих компонентов нужно. Я описывать процесс установки не буду, т.к. сам ставил давно, и уже не помню всех подробностей, а ставить заного ради статьи – лень  Все что нужно знать о установке подробно расписано в README. Единственное для mediaproxy почему-то нет init файлов, но это не беда, их легко написать самостоятельно. 

Конфигурирование openser
Перейдем к основному, как настроить kamailio. Где-то я возможно повторюсь с статьей о openser с voip.rus.net, но там описан процесс настройки применительно к старым версиям Openser, в новых версиях kamailio многое изменилось и процесс настройки слегка изменился, особенно это касается работы c NAT.
Конфигурационный файл я аттачить не буду, а буду приводить куски в статье, если эти куски собрать во едино, то получится нужный конфигурационный файл. Делаю это я осознанно, чтобы не возникало желания пролистать статью, ибо «много букфф»  залить конфигурационный файл себе и получить все косяки из-за слабого представления что где зачем.
Я буду описывать не каждую строчку, т.к. предназначение некоторых банально, а некоторые я и сам не знаю зачем нужны, но в мануалах пишут что они нужны 
Настоятельно рекомендую тем кто не знаком с процедурой установления соединения SIP протокола прочитать соответствующие мануалы. Не повредит знать структуру SIP пакетов т.к. kamailio для маршрутизации звонков использует те или иные поля SIP пакета.
Пожалуй начнем.

debug=3
log_stderror=no

отправлять или нет log сообщения в stdout, если стоит нет, есть возможность отправлять лог сообщения на syslog

fork=yes

директива fork заставляет kamailio работать в режиме демона, иначе все сообщения будут попадать в

stdoutchildren=8
disable_dns_blacklist=yes

disable_dns_blacklist=yes без этой опции kamailio может коряво работать с серверами на которые будем маршрутизировать сообщения

auto_aliases=no
port=5060
listen=udp:192.168.0.1:5060

listen – понятен наверное для чего, параметров listen может быть несколько, но надо быть осторожным с ip маршрутами, чтобы сообщение поступив через один интерфейс не уходило обратно через другой, если конечно именно не это требуется

alias=voip.telecom.ru:5060
alias= sip.telecom.ru:5060
alias=192.168.0.1:5060

список alias-ов, синонимом которых является сервер

mpath="/usr/local/lib/kamailio/modules/"

 Путь до папки с модулями

kamailioloadmodule "pv.so"
loadmodule "db_mysql.so"
modparam("db_mysql", "auto_reconnect", 1)

загружаем модуль работы с mysql и включаем авто реконнект.

loadmodule "sl.so"
loadmodule "tm.so"
modparam("tm", "fr_timer", 10)
modparam("tm", "fr_inv_timer", 120)
modparam("tm", "wt_timer", 5)
modparam("tm", "delete_timer", 2)
modparam("tm", "ruri_matching", 1)
modparam("tm", "via1_matching", 1)
modparam("tm", "unix_tx_timeout", 2)
modparam("tm", "restart_fr_on_each_reply", 1)
modparam("tm", "pass_provisional_replies", 0)
loadmodule "rr.so"
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 0)
loadmodule "maxfwd.so"
modparam("maxfwd", "max_limit", 256)
loadmodule "usrloc.so"
modparam("usrloc", "db_mode",   1)
modparam("usrloc", "timer_interval",   120)
modparam("usrloc", "db_url", "mysql://openser:openserrw@localhost/openser")
modparam("usrloc", "nat_bflag", 6)
modparam("usrloc", "expires_column", "expires")

задаем параметры базы данных, куда конектится, какую базу использовать, выбираем для флага сигнализирующего принадлежность клиента к клиентам за натом bflag 6, подробнее о флагах можно почитать на сайте

kamailioloadmodule "registrar.so"
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 50)
modparam("registrar", "default_q", 0)
modparam("registrar", "append_branches", 1)
modparam("registrar", "case_sensitive", 0)
modparam("registrar", "max_contacts", 0)
modparam("registrar", "retry_after", 0)
modparam("registrar", "method_filtering", 0)

модуль отвечающий за обработку сообщений Register, у меня выставлен таймер максимального времени перерегистрации в 50 секунд, это связано с тем что мы используем у себя переделанный клиент [QuteCom]?, который если регается реже начинает терять регистрацию и валится в корку. max_expires заставляет всех клиентов регаться не реже чем раз в 50 секунд.

loadmodule "textops.so"
loadmodule "xlog.so"
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
modparam("mi_fifo", "fifo_mode", 0660)
modparam("mi_fifo", "fifo_group", "openser")
modparam("mi_fifo", "fifo_user", "openser")
modparam("mi_fifo", "reply_dir", "/tmp/")
modparam("mi_fifo", "reply_indent", "\t")

/tmp/kamailio_fifo – это сокет для управления и мониторинга kamailio, управляет и мониторит приложении

kamctlloadmodule "uri_db.so"
modparam("uri_db", "use_uri_table", 0)
modparam("uri_db", "db_url", "")
loadmodule "siputils.so"
loadmodule "nathelper.so"
modparam("nathelper", "rtpproxy_disable", 1)
modparam("nathelper", "natping_interval", 10)
modparam("nathelper", "received_avp", "$avp(i:42)")

Т.к. будем использовать mediaproxy, то выключаем поддержку rtpproxy, задаем интервал «пингания» UA, чтобы роутеры держали открытыми порты.

loadmodule "avpops.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
modparam("dispatcher", "flags", 2 )
modparam("dispatcher", "list_file", "/usr/local/etc/kamailio/dispatcher.list")
modparam("dispatcher", "dst_avp", "$avp(i:271)")
modparam("dispatcher", "grp_avp", "$avp(i:272)")
modparam("dispatcher", "cnt_avp", "$avp(i:273)")

модуль dispatcher служит для реализации load balancing-а, в list file хранятся адреса различных серверов на которые мы будем распределять вызовы.

loadmodule "auth_radius.so"
modparam("auth_radius", "radius_config","/etc/radiusclient-ng/radiusclient.conf")
modparam("auth_radius", "service_type",1)
modparam("auth_radius", "use_ruri_flag", 22)

включаем поддержку radius, указываем путь до конфига радиус сервер

аloadmodule "mediaproxy.so"
modparam("mediaproxy","mediaproxy_socket", "/var/run/mediaproxy/dispatcher.sock")

включаем работу с mediaproxy, задаем путь до сокета media-dispatcher

loadmodule "domain.so"
modparam("domain", "db_url", "mysql://openser:openserrw@localhost/openser")
modparam("domain", "db_mode", 1)
modparam("domain", "domain_table", "domain")
modparam("domain", "domain_col", "domain")
loadmodule "presence.so"
modparam("presence", "db_url", "mysql://openser:openserrw@localhost/openser")
modparam("presence", "max_expires", 3600)
modparam("presence", "server_address", "sip:sippalanser.is74.ru:5060")
loadmodule "dialog.so"
modparam("dialog", "dlg_flag", 4)
loadmodule "nat_traversal.so"
modparam("nat_traversal", "keepalive_interval", 90)
modparam("nat_traversal", "keepalive_method", "OPTIONS")

Kamailio читает конфигурационный файл от начала до конца, соответственно диалплан исполняется как обычная программа. Существует несколько разновидностей блоков маршрутизации, которые зовутся: route – основной блок маршрутизации, route – что-то типа процедур, только без параметров, все параметры передаются с помощью флагов и переменных, t_on_reply – блок для обработки различных ответов, failure_route – для обработки ошибок

route
{

Основной блок маршрутизации, по аналогии с С/C++ main() {}

if (method=="OPTIONS")
{
	exit;
    };

if (method=="PUBLISH")
{	exit;    };

if (method=="SUBSCRIBE")
{
	exit;
    };

Я не использую у себя эти сообщения пока, чтобы не мешались оправляю их в dev/null

if (\!mf_process_maxfwd_header("10"))
{
	sl_send_reply("483","Too Many Hops");
	exit;
    };

if (msg:len > max_len )
{
	sl_send_reply("513", "Message Overflow");
	exit;
    };

Небольшая защита от больших пакетов и зацикленных вызовов.

# \----------------------------------------------------------------\-
# Record Route Section
# \----------------------------------------------------------------\-
if (method=="INVITE" && nat_uac_test("2"))
{
xlog("L_INFO", "record route section \| INVITE & nat test: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
record_route_preset("192.168.0.1:5060;nat=yes");
}

Если получено сообшение INVITE и процедура проверки ната вернула нам истину, то явно указываем заголовочное поле Record-Route. (nat_uac_test может по разному определять ваш нат, для этого у него есть несколько методов определения ната, советую почитать документацию и опытно теоретическим путем выяснить какой аргумент этой функции вам подойдет). Функция xlog отсылает указанное сообщение в аргументе на syslog или на stdout

else if (method\!="REGISTER")
{
record_route();
}

Если от SIP клиента, находящегося за маршрутизатором с NAT, получено сообщение не INVITE и не REGISTER типа, то только тогда мы вызываем функцию record_route(), для гарантии того, чтобы сообщения проходили через наш SIP прокси сервер с вышестоящих и нижестоящих SIP прокси серверов или со шлюзов в публичную телефонную сеть (PSTN).

if (method=="BYE" \|\| method=="CANCEL")
{
xlog("L_INFO", "Call Tear Down \| end_media_session: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
end_media_session();
};

Отмена или конец вызова, вызывается end_media_session, это фунцкия модуля mediaproxy, даже если у нас не был mediaproxy задействован для установления вызова совершенно безопасно на всякий случай вызывать эту функцию чтобы наверняка разорвать соединение.

# ----------------------------------------------------------------\-
# Loose Route Section
#Секция свободной маршрутизации
# ----------------------------------------------------------------\-

Эта секция включается когда сообщение адресовано не нашему серверу, а проходит через него транзитом

if (loose_route())
{
xlog("L_INFO", "loose route\n");
if ((method=="INVITE" \|\| method=="REFER") && \!has_totag())
{
	    sl_send_reply("403", "Forbidden");
	    return;
	};

Мы должны особым образом обрабатывать сообщения Re-Invite, дабы предотвратить разрыв потоков по RTP протоколу, во время обработки этих сообщений. Таким образом, в этом месте мы отдельно обрабатываем эти сообщения для клиентов, находящихся за NAT.
Для гарантии того, что мы получили действительно повторное сообщение INVITE (re-INVITE), мы должны убедиться, что функция has_totag() и loose_route() вернула TRUE. Причина в том, что возможно оригинальное сообщение INVITE содержит предопределенные заголовочные поля для маршрутов, что заставило бы loose_route() вернуть TRUE. Поэтому производим проверку функцией has_totag(), т.к. только для уже установленных соединений будет содержаться флаг "tag=" в заголовочном поле <To> (т.е., только для вызовов, где, вызываемым абонентом, был подтвержден запрос на установку соединения сообщением с кодом "200 OK").
Другими словами, эта новая проверка безопасности основывается на том факте, что уже установленные SIP вызовы будут содержать "totag", тогда как еще не установленные - его не содержат. Для гарантии того, что наша логика "свободной маршрутизации" не будет использована в злонамеренных целях, мы проверяем тот факт, что эти сообщения INVITE и REFER, приняты в рамках уже установленного соединения.

if (method=="INVITE"){
if (nat_uac_test("2") \|\| search("^Route:.*;nat=yes")){

Теперь мы проверяем NAT статус отправителя сообщения re-INVITE, вызывая функцию nat_uac_test("3"). Также ищем заголовочное поле <Route>, содержащий тег ";nat=yes", который будет вставлен ранее, обсуждаемой ранее, функцией record_route_preset(). Если найден тэг ";nat=yes", тогда вызывающий абонент находиться за маршрутизатором с NAT.

setbflag(6);

Если отправитель сообщения находиться за маршрутизатором с NAT или INVITE сообщение содержит флаг "nat=yes", тогда мы устанавливаем флаг 6 для использование его в дальнейшем.

use_media_proxy();

Для начала проксирования RTP потоков, мы вызываем функцию use_media_proxy(). Она будет общаться с внешним mediaproxy сервером, заставляя это открыть UDP порты для обоих клиентов, или управлять существующим сеансом RTP проксирования для уже установленного вызова, в зависимости от заголовочного поля <Call-ID>. Вызов use_media_proxy() вызывает перезапись содержимого SDP, в части IP адреса и портов, которые выделил mediaproxy сервер для RTP потоков

	    }
else{
		xlog("L_INFO", "loose route |  Re-INVITE from NO NAT: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
		route(4);
	    }


}
route(1);
exit;
}

Если NAT -а нет, то просто отправляем звонок на route[4] для проверки находится ли абонент за NAT и на route[1] для доставки сообщения по назначению, что будет происходить там, мы выясним чуть позже.

# --\--\--\----------------------------------------------------------\-
# Call Type Processing Section
# Секция, обрабатывающая различные типы вызовов.
# \----------------------------------------------------------------\-
if (uri\!=myself)
{
xlog("L_INFO", "MESSAGE NOT MYSELF: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
route(4);
route(1);
exit;
};

Если сообщение больше не нуждается в обработке нашим SIP серверов, то отправляем его на route

if (method=="ACK")
{
xlog("L_INFO", "route ACK detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
route(1);
exit;
}

Получено сообщение ACK, отдельное правило создано для нужд отладки, такие сообщение мы должны сразу на route отдать.

else if (method=="CANCEL")
{
xlog("L_INFO", "route CANCEL detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
end_media_session();
route(1);
exit;
}

Получено команда отмены вызова, вызываем end_media_session, чтобы наверняка закрыть UDP порты на mediaproxy, даже если UA не за NAT и mediaproxy не использовался, совершенно безопасно вызвать эту команду и отправляем Cancel по назначению чтобы другой UA тоже закончил процедуру установления сессии.

else if (method=="INVITE")
{
route(3);
exit;
}

Если получено сообщение INVITE то отдаем его сразу на route, там содержится вся логика установления соединения.

else if (method=="REGISTER")
{
xlog("L_INFO", "route REGISTER detect: M=$rm RURI=$ru F=$fu T=$tu IP=$si $mb\n");
route(2);
exit;
}

Если получено сообщение REGISTER то будем его обрабатывать в route все остальные сообщения отправляем по назначению.

# \----------------------------------------------------------------\-
# Default Message Handler
# \----------------------------------------------------------------\-
route[1]
{
xlog("L_INFO", "route[1] default handler: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
t_on_reply("1");

При работе с UA находящимися за NAT мы должны корректно обрабатывать сообщения возвращающиеся к UA, к этим сообщениям можно получить доступ через блок reply_route

if (\!t_relay())

вызываем функцию t_relay, это statefull функция, т.е. с сохранением состояния транзакции. Т.е. если после начала транзакции Invite сообщением отправить ACK или BYE то это сообщение будет отправлено именно тому UA который это сообщение ждет.

 {
xlog("L_INFO", "route[1] \| ERROR: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
if (method=="INVITE" \|\| method=="ACK"){
	    xlog("L_INFO", "route[1] | INVITE or ACK error: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
	    xlog("L_INFO", "route[1] | end_media_session: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
	    end_media_session();
	}


sl_reply_error();

Ошибка доставки сообщения

}
}

# \-----------------------------------------------------------------------\-
# Обработка REGISTER
# \-----------------------------------------------------------------------\-
route[2]
{
sl_send_reply("100", "Trying");
xlog("L_INFO", "route[2] REGISTER Message Handler M=$rm RURI=$ru F=$fu T=$tu IP=$si");
if (\!search("^Contact:\[ \]**") && nat_uac_test("2")) {
	setbflag(6);
	fix_nated_register();

Fix_nated_register() специально используется для обработки сообщений REGISTER от клиентов, находящихся за NAT force_rport();
функция Force_rport () добавляет полученный IP порт в самое начало заголовочных полей "via" SIP сообщения. Это дает возможность направлять последующие SIP сообщения на нужный порт для последующих SIP транзакций.

fix_contact();

переписываем IP и порт в заловке Contact, чтобы в таблице зарегистрированных UA был не локальный адрес за натом, а адрес ната и порт через который можно достучаться до UA

}

Проверяем UA от которого пришло сообщение, за NAT он или нет, если проверка истина то выставляем bflag 6, этот флаг будет сохранен в таблице location, kamailio всегда будет знать какой UA за Nat или нет, чтобы иметь возможность задействовать mediaproxy для установления RTP сессии

if(is_method("REGISTER") && is_present_hf("Expires") && $(hdr(Expires){s.int})==0){
	xlog("L_INFO", "UNREGISTER: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
    }

Если поле Expires=0 значит UA отрегивается, не будем уточнять его параметры авторизации.

    else
{
if(\!radius_www_authorize("")){
	    xlog("L_INFO", "radius_www_authorize() error M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
	    www_challenge("","1");
	    exit;
	}
else
{
		sl_send_reply("401", "Unauthorized");
		exit;
	    }consume_credentials();	" macrohasbody="false" wikihasprecedingnewline="false" wikihastrailingnewline="true" >{ 	     if (\!check_to())
{		sl_send_reply("401", "Unauthorized");		exit;	    }


consume_credentials(); 	} };

В противном случае авторизуем его. У нас авторизация реализована через Radius, только не спрашивайте как настраивать Radius сервер какие атрибуты надо править. У нас отдельный человек занимается Radius-ом, и настройка всего этого его рук дело. Наш Radius сервер претерпел множественные изменения исходников для нужд нашей компании, поэтому его конфиги вам совершенно не будут интересны. В книге про openser есть пример как использовать аккаунты в базе для регистрации, на сайте есть туториал по работе с radius в нете полно примеров разных конфигураций, так что в этом у вас есть полная свобода.

    if (\!save("location")){
	sl_reply_error();
    }
}

Сохраняем информацию о UA в базе kamailio, в таблице locations.

Секция обработки INVITE route[3]

{
xlog("L_INFO", "route[3] invite handler: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");

if (nat_uac_test("2")){
	 setbflag(7);

здесь мы выставляем bflag 7, т.е. когда исходящий вызов сигнализатором NAT-а будет флаг 7, а при входящем вызове на абонента за NAT-ом флаг 6

    	force_rport();
    	fix_contact();
   }

Если INVITE от клиента за NAT делаем с ним то же что сделали до этого в route. Здесь было бы неплохо сделать авторизацию INVITE, дабы кто попало не позвонил и не поговорил на халяву, если у вас конечно этот сервер будет обслуживать клиентов, которым надо насчитать денег. Но на практике kamailio не до конца справляется с авторизацией INVITE. 20-30 % звонков завершаются аварийно, т.к. kamailio решает что авторизация не пройдена. Товарищ tma c форума asterisk-support.ru пишет «Проблема в том, что INVITE может придти без необходимых для Digest авторизации данных, в результате биллинг/radius "отшивает" запрос, а Kamailio/SER/[OpenSER], как следствие, его рвет.» На практике авторизация работает примерно так, что для REGISTER что для INVITE запросов. Приходит первоначальный запрос, если в нем нет необходимой Digest информации для авторизации запроса, то сервер должен вернуть false, потом он отправляет 401 Unauthorized, на что клиент должен попробовать еще раз отправить этот запрос но уже с необходимыми Digest данными, для запросов REGISTER это прокатывает, а вот для INVITE-ов почему-то не всегда, сложный вопрос почему это так, я не знаю причин и пока решил вызовы не проверять, есть идеи как такую проверку реализовать, но это уже отдельная тема

Lookup(“location”) заставляет сервер проверить есть ли UA которому хочет позвонить клиент отправивший INVITE в списке зареганных, если он есть то функция возвращает его реквизиты, а также значение bflag 6, установлен или нет

    if (\!lookup("location")){

UA не найден, значит либо он не зареган либо звонят не UA находящемуся на этом сервере

	xlog("L_INFO", "route[3] | not local client: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
        route(5);
	exit;

если нужного номера нет на этом сервере, то поищем его на asterisk сервере, route[[5]|http://trac.asteriskpbx.ru/changeset/5] отвечает за перенаправление вызова на asterisk

     }
else{
	xlog("L_INFO", "route[3] | local client: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
    }

клиент найден на сервер, отправляем его на route , который в случае необходимости включит mediaproxy и собственно по назначению на route

    route(4);
route(1);
}

Включаем mediaproxyroute[4]

{
#xlog("L_INFO", "route[4] nat traversal: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
if (isbflagset(6) \|\| isbflagset(7))
{

Проверяем, если установлен bflag 6, то вызов на UA который за NAT, если установлен bflag 7, то вызов от UA который за NAT

        if (\!isbflagset(8)){
	    setbflag(8);
	    xlog("L_INFO", "route[4] | use_media_proxy: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
	    use_media_proxy();
	}

Включаем mediaproxy, простенькая защита от многократного включения mediaproxy для одного вызова, реализована посредством конструкции c bflag 8

    }
}

Вот собственно секция для выбора нужной ноды asterisk, работает модуль dispatcherroute[5]

{
     t_on_reply("2");
     t_on_failure("1");

обработка ошибок и сообщений

    xlog("L_INFO", "route[5]->asterisk node: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
    ds_select_domain("1","4");

выбираем группу серверов 1 и механизм распределения вызовов 4 – round robin

    route(4);
    route(1);

отправляем вызов куда следует

}

В файле dispatcher.lis который мы указали здесь modparam("dispatcher", "list_file", "/usr/local/etc/kamailio/dispatcher.list") мы прописываем необходимые сервера куда будем балансировать нагрузку.1 sip:192.168.0.1:5060
1 sip:192.168.0.2:5060
Вот на эти два сервера и пойдут запросы2 sip:192.168.0.3:5060
2 sip:192.168.0.4:5060
А сюда бы они пошли если бы мы указали в конфиге kamailio ds_select_domain("2","4");
Обработка ошибок и сообщений.onreply_route[1]

{
if ((isbflagset(6) \|\| isbflagset(7)) && (status=~"(180)\|(183)\|2[0-9|0-9][0-9|0-9]"))
{
if (\!search("^Content-Length:\[ \]*0"))
{
	    use_media_proxy();
	};
if (nat_uac_test("2"))
{
	    fix_contact();
	};
};
}

onreply_route[2]
{
if (status=~"[12][0-9|0-9][0-9|0-9]")
{
        fix_nated_contact();
        exit;
    };
}


failure_route[1]
{
if( t_check_status("408") ){
	xlog( "L_NOTICE", "[$Tf] FR: $ci -- TIMEOUT for Gateway $rd\n" );
    }
else
{
	xlog( "L_NOTICE", "[$Tf] FR: $ci -- $rs reason $rr\n" );
    };
if( t_check_status("403") )
{
	xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Forbidden -> ISDN Cause Code 1\n" );
    	return;
    };
if( t_check_status("486") )
{
	xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Destination BUSY \n" );
	return;
    };
if( t_check_status("487") )
{
	xlog("L_NOTICE", "[$Tf] FR: $ci -- SIP-$rs Request Cancelled\n" );
	return;
    };
if( ds_next_domain() )
{
t_on_reply("2");
xlog( "L_NOTICE", "[] FR: $ci Next gateway $fU \-> $tU via $rd\n" );
if( \!t_relay() )
{
	    xlog( "L_INFO", "[$Tf] FR: $ci -- ERROR - Can not t_relay()\n" );
	    return;
	};
return;
}
else
{
             xlog( "L_INFO", "[$Tf] FR: $ci No more buscuits in the gateways" );
	t_reply("503", "Service unavailable -- no more gateways" );
	exit;
    };
xlog("L_INFO", "failure_route[1]\->: M=$rm RURI=$ru F=$fu T=$tu IP=$si\n");
}

Вот в принципе минимальный конфиг для того чтобы ваш kamailio стал полноценным load балансером. Но это лишь мизер всех его возможностей.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.