Кластеризация opensips и скрытие топологии в операторской sip-сети

в 11:50, , рубрики: ip-телефония, системное администрирование

Наша сеть построена на opensips 1.8. Основная нагрузка на opensips приходится при обработке регистраций, в отличие от звонков, на которые приходится гораздо меньшая нагрузка. Поэтому, когда в нашей сети количество регистраций перешагнуло определенный порог, было принято решение о горизонтальном масштабировании opensips. Так родился проект opensips-кластера.

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

Одновременно с этим, возникла идея о скрытии топологии. В opensips для этого есть два механизма.

Первый – недавно появившаяся функция topology_hiding() в модуле dialog.

Второй – модуль b2b_logic, реализующий полноценный b2b.

После испытаний, было принято окончательное решение использовать b2b_logic. Однако, мы столкнулись с ограничениями в его функциональности. Например, не было возможности полноценно работать с sip-заголовками (удалять, добавлять, изменять), а также не было режима прозрачной аутентификации. Аутентификация была возможна на сервере с b2b, но пробрасывать ее на следующий сервер b2b не умел. Были написаны соответствующие патчи к основному коду, которые разработчики opensips обещают включить в релиз 1.9.

Также следует отметить, что opensips построен так, что на одном запущенном экземпляре нельзя использовать b2b и прокси одновременно. В итоге, родилась схема, которая стала для нас рабочей.

Кластеризация opensips и скрытие топологии в операторской sip сети

B2B используется для балансировки sip-трафика между нодами кластера, скрывает топологию сети в обе стороны, нормализует и фильтрует некоторые sip-заголовки, отвечает за определение NAT, поддерживает SIP/UDP, SIP/TCP, SIP/TLS протоколы, а также STUN-сервер.

В качестве нод используются opensips в режиме прокси, они отвечают за сервис определения местоположения клиента (location), нормализуют, корректируют sip-заголовки от клиентов, на которых некорректно реализована поддержка sip-протокола, управляют RTP-проксированием и обрабатывают NAT.

В этой схеме SIP-пакет проходит от клиента к class5 софтсвитчу таким образом:
софтсвитч -> балансировщик -> нода -> балансировщик -> клиент
либо в обратном порядке.

Каждый opensips размещен на своем аппаратном сервере, между серверами настроена master-master репликация postgresql.

Конфигурационные файлы у нас генерируются с использованием m4. На каждом сервере лежит файл со статическими переменными, специфичными для данного сервера, а общие части конфигурации хранятся в git. В упрощенном виде, конфигурации привожу здесь.

balancer.cfg

####### Global Parameters ##############################################
debug=3
log_stderror=no
log_facility=LOG_LOCAL0
fork=yes
children=3
disable_tcp=no
# TCP
tcp_children=10
tcp_accept_aliases=yes
tcp_send_timeout=5
tcp_connect_timeout=5
tcp_max_connections=4096
tcp_poll_method=epoll_et

mhomed=1
port=5060

listen = udp:192.168.0.1:5060
listen = udp:8.8.8.8:5060

listen = tcp:192.168.0.1:5060
listen = tcp:8.8.8.8:5060

server_header="Server: Cool SBC"
user_agent_header="User-Agent: Cool SBC"

disable_core_dump=no

####### Modules Section ################################################
mpath="/usr/lib64/opensips/modules"

########################################################################
loadmodule "maxfwd.so"
########################################################################
modparam("maxfwd", "max_limit", 256)

########################################################################
loadmodule "sipmsgops.so"
########################################################################

########################################################################
loadmodule "textops.so"
########################################################################

########################################################################
loadmodule "stun.so"
########################################################################
modparam("stun","primary_ip","8.8.8.8")
modparam("stun","primary_port","5060")
modparam("stun","alternate_ip","192.168.0.1")
modparam("stun","alternate_port","3478")

########################################################################
loadmodule "mi_fifo.so"
########################################################################
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
modparam("mi_fifo", "fifo_group", "opensips")
modparam("mi_fifo", "fifo_user", "opensips")
modparam("mi_fifo", "reply_dir", "/tmp/")
modparam("mi_fifo", "reply_indent", "t")

########################################################################
loadmodule "db_postgres.so"
########################################################################

########################################################################
loadmodule "avpops.so"
########################################################################

########################################################################
loadmodule "rr.so"
########################################################################
modparam("rr", "append_fromtag", 1)
modparam("rr", "enable_double_rr", 1)
modparam("rr", "add_username", 1)

########################################################################
loadmodule "sl.so"
########################################################################
modparam("sl", "enable_stats", 1)

########################################################################
loadmodule "tm.so"
########################################################################
modparam("tm", "fr_timer", 30)
modparam("tm", "fr_inv_timer", 120)
modparam("tm", "wt_timer", 5)
modparam("tm", "delete_timer", 2)
modparam("tm", "T1_timer", 500)
modparam("tm", "T2_timer", 4000)
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)
modparam("tm", "syn_branch", 1)
modparam("tm", "onreply_avp_mode", 0)
modparam("tm", "disable_6xx_block", 0)
modparam("tm", "enable_stats", 1)
modparam("tm", "fr_timer_avp", "$avp(fr_timer)")


########################################################################
loadmodule "signaling.so"
########################################################################

########################################################################
loadmodule "path.so"
########################################################################
modparam("path", "use_received", 1)
modparam("path", "enable_double_path", 1)

########################################################################
loadmodule "domain.so"
########################################################################
modparam("domain", "db_url", "postgres://opensips:opensips@192.168.0.2/opensips")
modparam("domain", "db_mode", 1)
modparam("domain", "domain_table", "domain")
modparam("domain", "domain_col", "domain")

########################################################################
loadmodule "cachedb_local.so"
########################################################################
modparam("cachedb_local", "cache_table_size", 9)
modparam("cachedb_local", "cache_clean_period", 86400)

########################################################################
loadmodule "b2b_entities.so"
########################################################################
modparam("b2b_entities", "db_url", "postgres://opensips:opensips@192.168.0.2/opensips_balancer")
modparam("b2b_entities", "db_mode", 1)
modparam("b2b_entities", "server_hsize", 14)
modparam("b2b_entities", "client_hsize", 14)
modparam("b2b_entities", "script_req_route", "B2B_REQUEST")
modparam("b2b_entities", "script_reply_route", "B2B_REPLY")
modparam("b2b_entities", "b2b_key_prefix", "sbc")

########################################################################
loadmodule "b2b_logic.so"
########################################################################
modparam("b2b_logic", "db_url", "postgres://opensips:opensips@192.168.0.2/opensips_balancer")
modparam("b2b_logic", "db_mode", 1)
modparam("b2b_logic", "hash_size", 14)
modparam("b2b_logic", "cleanup_period", 60)
modparam("b2b_logic", "use_init_sdp", 1)
modparam("b2b_logic", "init_callid_hdr", "x-orig-ci")
modparam("b2b_logic", "max_duration", 86400)
modparam("b2b_logic", "b2bl_from_spec_param", "$avp(hdrfrom)")
modparam("b2b_logic", "custom_headers", "Replaces;x-orig-to;x-src-uri")

########################################################################
loadmodule "nathelper.so"
########################################################################

########################################################################
loadmodule "dispatcher.so"
########################################################################
modparam("dispatcher", "db_url", "postgres://opensips:opensips@192.168.0.2/opensips_balancer")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "force_dst", 0)
modparam("dispatcher", "use_default", 0)
modparam("dispatcher", "dst_avp", "$avp(disp_dst)")
modparam("dispatcher", "attrs_avp", "$avp(disp_attrs)")
modparam("dispatcher", "grp_avp", "$avp(disp_grp)")
modparam("dispatcher", "cnt_avp", "$avp(disp_cnt)")
modparam("dispatcher", "hash_pvar", "$avp(disp_hash)")
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_ping_from", "sip:balancer@cool.sip")
modparam("dispatcher", "ds_ping_interval", 1)
modparam("dispatcher", "ds_probing_sock", "udp:192.168.0.1:5060")
modparam("dispatcher", "ds_probing_threshhold", 1)
modparam("dispatcher", "ds_probing_mode", 0)
modparam("dispatcher", "options_reply_codes", "200")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "setid_col", "setid")
modparam("dispatcher", "destination_col", "destination")
modparam("dispatcher", "flags_col", "flags")
modparam("dispatcher", "weight_col", "weight")
modparam("dispatcher", "attrs_col", "attrs")
modparam("dispatcher", "socket_col", "socket")
########################################################################

route {
	$var(from_proxy) = ds_is_in_list("$si", "$sp");
	$var(nolog) = 0;
	if ( ($var(from_proxy) == 1) && (is_method("OPTIONS")) ) $var(nolog) = 1;
	if ($var(nolog) == 0) xlog("L_INFO", "[MAIN] Incoming request (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n$mb");

	force_rport();

	remove_hf("Path");
	route(VALIDATE);
	route(PING);

	if (is_method("REGISTER")) {
		if (!add_path_received()) { # For proxy to know source ip address
			xlog("L_ERR", "[MAIN] Cannot add path (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
			send_reply("503", "Internal Path Error");
			exit;
		}
		route(BALANCE);
		$avp(fr_timer) = 3;
		route(RELAY);
	}

	if (is_method("MESSAGE")) {
		if ($var(from_proxy) == 1) {
			# Topology hiding
			remove_hf("Authorization");
			remove_hf("Proxy-Authorization");
			$du = $ru;
			$rd = $fd;
		} else {
			append_hf("x-src-uri: sip:$si:$sp;transport=$protorn");
			route(BALANCE);
		}
		route(RELAY);
	}

	if (is_method("ACK")) exit; # Must be an ACK after 401
	if (is_method("SUBSCRIBE|PUBLISH")) {
		xlog("L_ERR", "[MAIN] Method not supported (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("501", "Method not supported here");
		exit;
	}
	if (!is_method("INVITE|OPTIONS")) {
		xlog("L_ERR", "[MAIN] Call leg/Transaction does not exist (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("481", "Call leg/Transaction does not exist");
		exit;
	}

	loose_route(); # Preloaded route (for path module)

	if (!is_method("OPTIONS")) append_hf("x-orig-to: $hdr(To)rn");

	if ($var(from_proxy) == 1) {
		if (is_method("OPTIONS")) { # Ping from proxy to UAC
			# Topology hiding
			$ru = $du;
			remove_hf("Via");
			remove_hf("To");
			append_hf("To: $durn", "From");
			remove_hf("Call-ID");
			append_hf("Call-ID: $(ci{s.md5})rn", "To");

			route(RELAY);
		}
		xlog("L_INFO", "[MAIN] Request from proxy (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
	} else {
		xlog("L_INFO", "[MAIN] Request from UAC or commutator (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		if (route(FROM_CLASS5)) {
			if ($fd == "192.168.0.1") { # Override INT_IP to default domain
				$avp(hdrfrom) = $hdr(From);
				avp_subst("$avp(hdrfrom)", "/<.*>/<sip:$fU@$td>/");
			}
		} else {
			# Fix NATed contact and SDP in client requests
			if (nat_uac_test("19")) {
				xlog("L_INFO", "[MAIN] NAT detected, fixed contact (oldct=$ct) (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				fix_nated_contact();
			}
			if ( (has_body("application/sdp")) && (nat_uac_test("8")) ) {
				xlog("L_INFO", "[MAIN] NAT detected, fixed SDP (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				fix_nated_sdp("10");
			}
		}
		route(BALANCE);
		append_hf("x-src-uri: sip:$si:$sp;transport=$protorn");
	}
	b2b_set_mode("a");
	b2b_init_request("top hiding");
	exit;
}

route[B2B_REQUEST] {
	xlog("L_INFO", "[B2B_REQUEST] Incoming B2B request (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n$mb");

	force_rport();
	if (!ds_is_in_list("$si", "$sp")) { # From UAC or commutator
		append_hf("x-src-uri: sip:$si:$sp;transport=$protorn");
	}
	xlog("L_INFO", "[B2B_REQUEST] Request leaving server (will be generated by b2b) (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
}

route[B2B_REPLY] {
	xlog("L_INFO", "[B2B_REPLY] Incoming B2B reply (STATUS="$rs $rr" M=$rm IP=$si:$sp F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs)n$mb");

	if (!ds_is_in_list("$si", "$sp")) { # From UAC or commutator
		append_hf("x-src-uri: sip:$si:$sp;transport=$protorn");
	} else {
		remove_hf("x-orig-ci");
	}
	xlog("L_INFO", "[B2B_REPLY] Reply leaving server (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
}

route[BALANCE] {
	$du = null; # Ignore preloaded routes

	if (avp_check("$si","re/192.168./g")) {
		$var(balance-dst) = "2"; # Internal network
	} else {
		$var(balance-dst) = "1"; # External network
	}

	if (!ds_select_dst("$(var(balance-dst){s.int})", "0")) {
		xlog("L_ERR", "[BALANCE] No active gateways (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("503", "Service unavailable" );
		exit;
	}

	xlog("L_INFO", "[BALANCE] Balance packet to $dd:$dp (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");

	return;
}

route[RELAY] {
	if ($var(nolog) == 0) xlog("L_INFO", "[RELAY] Request leaving server (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");

	t_on_reply("ON_REPLY");
	t_on_failure("ON_FAIL");
	if (!t_relay("0x01")) sl_reply_error();
	exit;
}

route[VALIDATE] {
	if ($ua =~ "friendly-scanner|sundayddr|sip-scan|iWar|sipsak") {
		xlog("L_ERR", "[VALIDATE] Attack attempt - Request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		drop();
	}

	if (msg:len > max_len) {
		xlog("L_ERR", "[VALIDATE] Message too big - Sending 513 Message Too Big (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("513", "Message Too Big");
		exit;
	}

	if (!mf_process_maxfwd_header("10")) {
		xlog("L_ERR", "[VALIDATE] Too many hops - Sending 483 Too Many Hops (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("483", "Too Many Hops");
		exit;
	}

	if(!sipmsg_validate()) {
		switch($retcode) {
			case -1:
				xlog("L_ERR", "[VALIDATE] The message is not RFC3261 compliant - request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				break;
			case -2:
				xlog("L_ERR", "[VALIDATE] Parsing error - request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				break;
			case -3:
				xlog("L_ERR", "[VALIDATE] Invalid SDP body - request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				break;
			case -4:
				xlog("L_ERR", "[VALIDATE] Invalid headers body - request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
				break;
			default:
				xlog("L_ERR", "[VALIDATE] Undefined error - request dropped (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		}
		drop();
	}
	return;
}

route[PING] {
	if (is_method("PING")) {
		xlog("L_INFO", "[PING] PING - Sending 200 OK (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
		send_reply("200", "OK");
		exit;
	}

	if (is_method("NOTIFY")) {
		if ($hdr(Event) == "keep-alive") {
			xlog("L_INFO", "[PING] NOTIFY - Sending 200 OK (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
			send_reply("200", "OK");
			exit;
		}
	}

	if ($var(from_proxy) != 1) {
		if (is_method("OPTIONS")) {
			xlog("L_INFO", "[PING] OPTIONS - Sending 200 OK (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
			send_reply("200", "OK");
			exit;
		}
	}

	return;
}

route[FROM_CLASS5] {
	if ($si == "192.168.0.3") return(1);
	
	return(-1);
}

route[TO_CLASS5] {
	if ($dd == "192.168.0.3") return(1);
	
	return(-1);
}

onreply_route { # GLOBAL
	# NAT processing
	if (!ds_is_in_list("$si", "$sp")) { # From UAC or commutator
		if (!route(FROM_CLASS5)) {
			# Fix NATed contact and SDP in client replies
			if (nat_uac_test("65")) fix_nated_contact();
			if ( (has_body("application/sdp")) && (nat_uac_test("8")) ) fix_nated_sdp("10");
		}
	}

}

onreply_route[ON_REPLY] {
	$var(from_proxy) = ds_is_in_list("$si", "$sp");
	$var(nolog) = 0;
	if ( ($var(from_proxy) != 1) && (is_method("OPTIONS")) ) $var(nolog) = 1;
	if ($var(nolog) == 0) xlog("L_INFO", "[ON_REPLY] Incoming reply (STATUS="$rs $rr" M=$rm IP=$si:$sp F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs)n$mb");

	if ($var(from_proxy) != 1) { # From UAC or commutator
		# Fix via in OPTIONS ping reply
		if ( (is_method("OPTIONS")) && ($fu == "sip:pinger@cool.sip") ) insert_hf("Via: SIP/2.0/UDP $si:$sprn");
	} else {
		if (!route(TO_CLASS5)) {
			if (is_method("MESSAGE")) remove_hf("Record-Route");
		}
	}
}

failure_route[ON_FAIL] {
	if (!t_check_status("408")) return;
	if (ds_is_in_list("$si", "$sp")) return; # OPTIONS pings

	xlog("L_ERR", "[ON_FAIL] Timeout for current gateway (M=$rm IP=$si:$sp F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs)n");
	ds_mark_dst("p"); # Set gw to probing state

	if (ds_next_dst()) {
		xlog("L_INFO", "[ON_FAIL] Next gateway is $dd:$dp (M=$rm IP=$si:$sp F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs)n");
		cache_store("local", "proxy;$ci", "$du");
		route(RELAY);
	} else {
		xlog("L_ERR", "[ON_FAIL] No more gateways (M=$rm IP=$si:$sp F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs)n");
		send_reply("503", "Service unavailable" );
		exit;
	}
}

local_route {
	if (is_method("OPTIONS")) return;
	if (is_present_hf("x-orig-ci")) { # Packet from B2B
		xlog("L_INFO", "[LOCAL] B2B generated request (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n$mbn");

		if ($hdr(x-orig-to)) { # Restore original To header after b2b
			remove_hf("To");
			append_hf("To: $hdr(x-orig-to)rn", "From");
			remove_hf("x-orig-to");
		}
		if (route(TO_CLASS5)) {
			remove_hf("Authorization");
			remove_hf("Proxy-Authorization");
		}

		if (!ds_is_in_list("$dd", "$dp")) { # Not to proxy
			remove_hf("x-orig-ci");
			remove_hf("x-src-uri");
		}
		xlog("L_INFO", "[LOCAL] Request leaving server (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(Init-CallID) cseq=$cs UA=$ua)n");
	} else {
		xlog("L_INFO", "[LOCAL] Generated request (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n$mbn");
		xlog("L_INFO", "[LOCAL] Request leaving server (M=$rm IP=$si:$sp RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci cseq=$cs UA=$ua)n");
	}
}

node.cfg

####### Global Parameters ##############################################
debug=3
memlog=0
log_stderror=no
log_facility=LOG_LOCAL0
fork=yes
children=3
disable_tcp=yes
#
mhomed=1
port=5060
listen = udp:192.168.0.100:5060
listen = udp:8.8.4.4:5060
server_header="Server: Gateway 1"
user_agent_header="User-Agent: Gateway 1"
#
disable_core_dump=no

####### Modules Section ################################################
mpath="/usr/lib64/opensips/modules"

########################################################################
loadmodule "maxfwd.so"
########################################################################
modparam("maxfwd", "max_limit", 256)

########################################################################
loadmodule "sipmsgops.so"
########################################################################

########################################################################
loadmodule "textops.so"
########################################################################

########################################################################
loadmodule "mi_fifo.so"
########################################################################
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
modparam("mi_fifo", "fifo_group", "opensips")
modparam("mi_fifo", "fifo_user", "opensips")
modparam("mi_fifo", "reply_dir", "/tmp/")
modparam("mi_fifo", "reply_indent", "t")

########################################################################
loadmodule "db_postgres.so"
########################################################################

########################################################################
loadmodule "drouting.so"
#########################################################################
modparam("drouting", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("drouting", "drd_table", "dr_gateways")
modparam("drouting", "drr_table", "dr_rules")
modparam("drouting", "drg_table", "dr_groups")
modparam("drouting", "drc_table", "dr_carriers")
modparam("drouting", "use_domain", 1)
modparam("drouting", "drg_user_col", "username")
modparam("drouting", "drg_domain_col", "domain")
modparam("drouting", "drg_grpid_col", "groupid")
modparam("drouting", "force_dns", 1)
modparam("drouting", "probing_interval", 30)
modparam("drouting", "probing_method", "OPTIONS")
modparam("drouting", "probing_from", "sip:pinger@cool.sip")
modparam("drouting", "probing_reply_codes", "501, 403")

########################################################################
loadmodule "avpops.so"
########################################################################
modparam("avpops", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("avpops", "avp_table", "usr_preferences")
modparam("avpops", "use_domain", 1)
modparam("avpops", "username_column", "username")
modparam("avpops", "domain_column", "domain")
modparam("avpops", "attribute_column", "attribute")
modparam("avpops", "value_column", "value")
modparam("avpops", "type_column", "type")
modparam("avpops", "buf_size", 1024)

########################################################################
loadmodule "domain.so"
########################################################################
modparam("domain", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("domain", "db_mode", 1)
modparam("domain", "domain_table", "domain")
modparam("domain", "domain_col", "domain")

########################################################################
loadmodule "usrloc.so"
########################################################################
modparam("usrloc", "nat_bflag", 6)
modparam("usrloc", "user_column", "username")
modparam("usrloc", "domain_column", "domain")
modparam("usrloc", "contact_column", "contact")
modparam("usrloc", "expires_column", "expires")
modparam("usrloc", "q_column", "q")
modparam("usrloc", "callid_column", "callid")
modparam("usrloc", "cseq_column", "cseq")
modparam("usrloc", "methods_column", "methods")
modparam("usrloc", "flags_column", "flags")
modparam("usrloc", "cflags_column", "cflags")
modparam("usrloc", "user_agent_column", "user_agent")
modparam("usrloc", "received_column", "received")
modparam("usrloc", "socket_column", "socket")
modparam("usrloc", "path_column", "path")
modparam("usrloc", "use_domain", 1)
modparam("usrloc", "desc_time_order", 0)
modparam("usrloc", "timer_interval", 60)
modparam("usrloc", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("usrloc", "db_mode", 3)
modparam("usrloc", "matching_mode", 1)
modparam("usrloc", "cseq_delay", 20)
modparam("usrloc", "hash_size", 9)

########################################################################
loadmodule "alias_db.so"
########################################################################
modparam("alias_db", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("alias_db", "append_branches", 1)

########################################################################
loadmodule "rr.so"
########################################################################
modparam("rr", "append_fromtag", 1)
modparam("rr", "enable_double_rr", 1)
modparam("rr", "add_username", 1)

########################################################################
loadmodule "sl.so"
########################################################################
modparam("sl", "enable_stats", 1)

########################################################################
loadmodule "tm.so"
########################################################################
modparam("tm", "fr_timer", 30)
modparam("tm", "fr_inv_timer", 120)
modparam("tm", "wt_timer", 5)
modparam("tm", "delete_timer", 2)
modparam("tm", "T1_timer", 500)
modparam("tm", "T2_timer", 4000)
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)
modparam("tm", "syn_branch", 1)
modparam("tm", "onreply_avp_mode", 0)
# TODO: 1 - the 6xx replies will be handled as any other negative reply - serial forking will be allowed. Logically, you need to break RFC3261 if you want to do redirects to announcement and voicemail services.
modparam("tm", "disable_6xx_block", 0)
modparam("tm", "enable_stats", 1)

########################################################################
loadmodule "signaling.so"
########################################################################

########################################################################
loadmodule "dialog.so"
########################################################################
modparam("dialog", "enable_stats", 1)
modparam("dialog", "hash_size", 4096)
modparam("dialog", "rr_param", "did")
modparam("dialog", "default_timeout", 43200)
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_update_period", 60)
modparam("dialog", "table_name", "dialog")
modparam("dialog", "call_id_column", "callid")
modparam("dialog", "from_uri_column", "from_uri")
modparam("dialog", "from_tag_column", "from_tag")
modparam("dialog", "to_uri_column", "to_uri")
modparam("dialog", "to_tag_column", "to_tag")
modparam("dialog", "from_cseq_column", "caller_cseq")
modparam("dialog", "to_cseq_column", "callee_cseq")
modparam("dialog", "from_route_column", "caller_route_set")
modparam("dialog", "to_route_column", "callee_route_set")
modparam("dialog", "from_contact_column", "caller_contact")
modparam("dialog", "to_contact_column", "callee_contact")
modparam("dialog", "from_sock_column", "caller_sock")
modparam("dialog", "to_sock_column", "callee_sock")
modparam("dialog", "h_id_column", "hash_id")
modparam("dialog", "h_entry_column", "hash_entry")
modparam("dialog", "state_column", "state")
modparam("dialog", "start_time_column", "start_time")
modparam("dialog", "timeout_column", "timeout")

########################################################################
loadmodule "registrar.so"
########################################################################
modparam("registrar", "default_expires", 3600)
modparam("registrar", "min_expires", 0)
modparam("registrar", "max_expires", 7200)
modparam("registrar", "default_q", 0)
modparam("registrar", "tcp_persistent_flag", -1)
modparam("registrar", "realm_prefix", "sip.")
modparam("registrar", "case_sensitive", 0)
modparam("registrar", "received_avp", "$avp(rcv)")
modparam("registrar", "received_param", "received")
modparam("registrar", "max_contacts", 10)
modparam("registrar", "retry_after", 0)

########################################################################
loadmodule "nathelper.so"
########################################################################
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "natping_processes", 1)
modparam("nathelper", "received_avp", "$avp(rcv)")
modparam("nathelper", "sipping_bflag", 6)
modparam("nathelper", "sipping_from", "sip:pinger@cool.sip")
modparam("nathelper", "sipping_method", "OPTIONS")
modparam("nathelper", "nortpproxy_str", "a=sdpmangled:yesrn")

########################################################################
loadmodule "rtpproxy.so"
########################################################################
modparam("rtpproxy", "rtpproxy_sock", "udp:192.168.0.200:22222")
modparam("rtpproxy", "rtpproxy_disable_tout", 20)
modparam("rtpproxy", "rtpproxy_timeout", "10")
modparam("rtpproxy", "rtpproxy_autobridge", 1)
modparam("rtpproxy", "rtpproxy_retr", 5)
modparam("rtpproxy", "nortpproxy_str", "a=sdpmangled:yesrn")
modparam("rtpproxy", "rtpp_notify_socket", "tcp:192.168.0.100:22222")

########################################################################
loadmodule "auth.so"
########################################################################
modparam("auth", "nonce_expire", 20)
modparam("auth", "rpid_suffix", ";party=calling;id-type=subscriber;screen=yes")
modparam("auth", "realm_prefix", "sip.")
modparam("auth", "rpid_avp", "$avp(rpid)")
modparam("auth", "calculate_ha1", 0)
modparam("auth", "disable_nonce_check", 1)

########################################################################
loadmodule "auth_db.so"
########################################################################
modparam("auth_db", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("auth_db", "user_column", "username")
modparam("auth_db", "domain_column", "domain")
modparam("auth_db", "password_column", "ha1")
modparam("auth_db", "password_column_2", "ha1b")
modparam("auth_db", "calculate_ha1", 0)
modparam("auth_db", "use_domain", 1)

########################################################################
loadmodule "uri.so"
########################################################################
modparam("uri", "db_url", "postgres://opensips:opensips@192.168.0.101/opensips")
modparam("uri", "db_table", "subscriber")
modparam("uri", "user_column", "username")
modparam("uri", "domain_column", "domain")
modparam("uri", "uriuser_column", "username")
modparam("uri", "use_uri_table", 0)
modparam("uri", "use_domain", 1)

########################################################################
loadmodule "snmpstats.so"
########################################################################
modparam("snmpstats", "sipEntityType", "registrarServer")
modparam("snmpstats", "sipEntityType", "proxyServer")
modparam("snmpstats", "MsgQueueMinorThreshold", 2000)
modparam("snmpstats", "MsgQueueMajorThreshold", 5000)
modparam("snmpstats", "dlg_minor_threshold", 500)
modparam("snmpstats", "dlg_major_threshold", 750)
modparam("snmpstats", "snmpgetPath", "/usr/bin/")
modparam("snmpstats", "snmpCommunity", "public")

########################################################################
loadmodule "mi_xmlrpc.so"
########################################################################
modparam("mi_xmlrpc", "port", 8080)

########################################################################
loadmodule "cachedb_local.so"
########################################################################
modparam("cachedb_local", "cache_table_size", 9)
modparam("cachedb_local", "cache_clean_period", 600)

route {
	xlog("L_INFO", "[MAIN] Incoming request (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n$mb");

	if (!mf_process_maxfwd_header("10")) {
		xlog("L_ERR", "[MAIN] Too many hops (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		send_reply("483", "Too Many Hops");
		exit;
	}

	force_rport();
	route(PING);

	if (!is_method("REGISTER")) {
		if (nat_uac_test("19")) {
			record_route(";nat=yes");
		} else {
			record_route();
		}
	}

	if (is_method("CANCEL|BYE")) unforce_rtp_proxy();

	$var(preload) = 0;

	if (has_totag()) {
		if (loose_route()) {
			if (nat_uac_test("19") || search("^Route:.*;nat=yes")) {
				fix_nated_contact();
				setbflag(6);
			}
			if (is_method("INVITE|UPDATE")) resetbflag(22); # reINVITE, UPDATE
			route(RELAY);
		} else {
			if (is_method("ACK")) route(CHECK_TRANS);
			send_reply("403", "Not Relaying");
		}
	}

	if (is_method("CANCEL")) route(CHECK_TRANS);

	t_check_trans();

	if (loose_route()) { # Preloaded route
		xlog("L_ERR", "Denied attempt to route with preloaded route");
		if (!is_method("ACK")) sl_send_reply("403", "Preloaded Route Denied");
		exit;
	}

	if (is_method("REGISTER")) route(REGISTER);
	if (is_method("INVITE")) {
		send_reply("100", "Trying");
		route(INVITE);
	}
	if (is_method("MESSAGE")) route(INVITE);

	xlog("L_ERR", "[MAIN] Call leg/Transaction does not exist (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
	send_reply("481", "Call leg/Transaction does not exist");

	exit;
}

route[REGISTER] {
	route(AUTH);
	if (!db_check_to()) {
		xlog("L_ERR", "[REGISTER] Spoofed to URI detected (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		send_reply("403", "Spoofed to URI Detected");
		exit;
	}
	consume_credentials();
	if ( (is_present_hf("Contact")) && (nat_uac_test("19")) ) {
		xlog("L_INFO", "[REGISTER] NAT detected (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		fix_nated_register();
		setbflag(6);
	}
	if($ua =~ "(dlink)|(MP204)") {
		xlog("L_INFO", "[REGISTER] Fixed Expires HDR on bad UAC (ct expire=$ct.fields(expires), hdr expire=$hdr(Expires)) (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		if (!save("location","rp0")) {
			sl_reply_error();
			exit;
		}
		append_to_reply("Contact: $ct;expires=$hdr(Expires)rn");
		send_reply("200", "OK");
	} else {
		if(!save("location", "p0"))
		{
			xlog("L_ERR", "[REGISTER] Saving contact failed (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			sl_reply_error();
			exit;
		}
	}
	xlog("L_INFO", "[REGISTER] Registration successful (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
	exit;
}

route[INVITE] {
	if (nat_uac_test("19")) {
		xlog("L_INFO", "[INVITE] NAT detected (oldct=$ct) (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		fix_nated_contact();
		setbflag(6);
	}
	if (route(FROM_COMM)) { # Commutator -> SIP
		xlog("L_INFO", "[INVITE] Call from commutator (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		if ((!is_domain_local("$rd")) || ($rd == "qip.ru")) {
			xlog("L_INFO", "[INVITE] Call to foreign domain (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			$ru = "sip:" + $rU + "@" + $rd;
			if ($rd =~ "sip.zebra.ru") $rp = 5062;
			route(RELAY);
		}
		if (db_does_uri_exist()) {
			xlog("L_INFO", "[INVITE] Callee is local user (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			route(LOOKUP);
			route(RELAY);
		} else {
			xlog("L_ERR", "[INVITE] User not found (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("404", "Not Found");
			exit;
		}
	} else { # SIP -> SIP
		xlog("L_INFO", "[INVITE] Call from SIP (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		if ($fd =~ ".*cool.sip") {
			xlog("L_INFO", "[INVITE] Call from local domain (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			route(AUTH);
			if (!db_check_from()) {
				xlog("L_ERR", "[INVITE] Spoofed from URI detected (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				send_reply("403", "Spoofed from URI Detected");
				exit;
			}
			$var(routing_retcode) = do_routing("", "W");
		} else {
			xlog("L_INFO", "[INVITE] Call from foreign domain (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			$var(routing_retcode) = do_routing("1", "W");
		}

		if($var(routing_retcode) != 1) {
			xlog("L_ERR", "[INVITE] Error loading gateways (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("503", "Termination Currently Unavailable");
			exit;
		}
	}
	exit;
}

route[FROM_COMM] {
	if ($hdr(x-src-uri)) $var(realip) = $(hdr(x-src-uri){uri.host});
	if ($var(realip) == "192.168.0.3") return(1);
	
	return(-1);
}

route[TO_COMM] {
	if ($dd == "192.168.0.3") return(1);
	
	return(-1);
}

route[PING] {
	if (is_method("PING")) {
		xlog("L_INFO", "[PING] PING - Sending 200 OK (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		send_reply("200", "OK");
		exit;
	}
	if (is_method("NOTIFY")) {
		if ($hdr(Event) == "keep-alive") {
			xlog("L_INFO", "[PING] NOTIFY - Sending 200 OK (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("200", "OK");
			exit;
		}
	}
	if (is_method("OPTIONS")) {
		xlog("L_INFO", "[PING] OPTIONS - Sending 200 OK (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		send_reply("200", "OK");
		exit;
	}
	return;
}

route[AUTH] {
	if(!www_authorize("", "subscriber")) {
		$var(auth_retcode) = $retcode;
		switch ($var(auth_retcode)) {
			case -5:
				xlog("L_ERR", "[AUTH] Generic error (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				break;
			case -4:
				xlog("L_ERR", "[AUTH] No credentials (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				www_challenge("", "1");
				break;
			case -3:
				xlog("L_INFO", "[AUTH] Stale nonce (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				www_challenge("", "1");
				break;
			case -2:
				xlog("L_ERR", "[AUTH] Invalid password (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				break;
			case -1:
				xlog("L_ERR", "[AUTH] Invalid user (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
				break;
			default:
				xlog("L_ERR", "[AUTH] Unknown error ($var(auth_retcode)) (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		}
		send_reply("403", "Forbidden");
		exit;
	}
	consume_credentials();
	return;
}

route[LOOKUP] {
	alias_db_lookup("dbaliases" , "");
	lookup("location");
	$var(lookup_retcode) = $retcode;
	switch ($var(lookup_retcode)) {
		case 1:
			xlog("L_INFO", "[LOOKUP] Contact found (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			break;
		case -1:
			xlog("L_ERR", "[LOOKUP] No contact found (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("404", "Not Found");
			exit;
			break;
		case -2:
			xlog("L_ERR", "[LOOKUP] Contact found, but method not supported (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("405", "Method Not Allowed");
			exit;
		case -3:
			xlog("L_ERR", "[LOOKUP] Internal error during processing (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			send_reply("404", "Not Found");
			exit;
		default:
			xlog("L_ERR", "[LOOKUP] Unknown error ($var(lookup_retcode)) (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
			exit;
	}
	return;
}

route[RELAY] {
	if ( (!isbflagset(22)) && (has_body("application/sdp")) ) {
		setbflag(22);
		xlog("L_INFO", "[RELAY] RTPproxy offer (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		rtpproxy_offer("cofr");
	}

	if (route(TO_COMM)) $ru = "sip:" + $rU + "@" + $rd + ":" + $rp + ";transport=udp"; # Ensure UDP

	if ($du != null) {
		$var(dest_ip) = $dd;
	} else {
		$var(dest_ip) = $rd;
	}

	# Choose interface
	if (avp_check("$var(dest_ip)", "re/192.168./g")) {
	    $var(balancer_ip) = "192.168.0.1";
	    force_send_socket("192.168.0.100");
	} else {
	    $var(balancer_ip) = "8.8.8.8";
	    force_send_socket("8.8.4.4");
	}

	if ($var(preload) == 1) { # Need to add preload route
		xlog("L_INFO", "[RELAY] Add preloaded route to request (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)nn");
		$var(dest_uri) = "sip:"+$dd+":"+$dp+";transport="+$dP;
		append_hf("Route: <sip:$var(balancer_ip);lr;received=$var(dest_uri)>rn", "Via");
		$var(preload) = 0;
	}
	$du = "sip:"+$var(balancer_ip)+":5060;transport=udp";

	remove_hf("x-src-uri");
	remove_hf("x-orig-to");
	remove_hf("x-orig-ci");

	xlog("L_INFO", "[RELAY] Request leaving server (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
	t_on_reply("ON_REPLY");
	t_on_failure("ON_FAIL");
	if (!t_relay("0x01")) {
		sl_reply_error();
		if ( (isbflagset(22)) && (is_method("INVITE")) ) unforce_rtp_proxy();
	}
	exit;
}

route[CHECK_TRANS] {
	t_on_reply("ON_REPLY");
	if (t_check_trans()) {
		route(RELAY);
	} else {
		xlog("L_ERR", "[CHECK_TRANS] Dropping misrouted request (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
	}
	exit;
}

onreply_route[ON_REPLY] {
	xlog("L_INFO", "[ON_REPLY] Incoming reply (STATUS="$rs $rr" M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs)n$mb");

	if (status != "100") route(11);
	if ( (status=~"(180)|(183)|2[0-9][0-9]") && (has_body("application/sdp")) ) {
		rtpproxy_answer("cofr");
	}
	remove_hf("x-src-uri");
	remove_hf("x-orig-to");
	remove_hf("x-orig-ci");
}

failure_route[ON_FAIL] {
	if (!t_check_status("408|500|503")) {
		xlog("L_INFO", "[ON_FAIL] No failover routing needed for this response code (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
		if(isbflagset(22)) unforce_rtp_proxy();
		exit;
	}
	if (route(TO_COMM)) {
		if (use_next_gw()) route(RELAY);
		xlog("L_ERR", "[ON_FAIL] Failed to select next PSTN gateway (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
	}
	if(isbflagset(22)) unforce_rtp_proxy();
}

local_route {
	if (is_method("OPTIONS")) return;
	xlog("L_INFO", "[LOCAL] Generated request (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n$mbn");
	xlog("L_INFO", "[LOCAL] Request leaving server (M=$rm IP=$si:$sp origIP=$hdr(x-src-uri) RURI=$ru DURI=$du F=$fu T=$tu oP=$oP pr=$pr dP=$dP rP=$rP ID=$ci origID=$hdr(x-orig-ci) cseq=$cs UA=$ua)n");
}

В ближайших планах ввод в эксплуатацию еще одного такого же кластера. Оба кластера будут разнесены географически, между ними трафик будет распределяться с помощью Round-Robbin DNS (SRV-записи).

А также:
— задействовать cachedb для кэширования
— добавить функционал для автоматической блокировки сканирующих ботов и несложных атак
— добавить функционал для блокировки перебиральщиков паролей

Автор: nikbyte

  1. Kamil:

    Неужели регистрации так трудно обрабатывать? Я делал тесты.. Opensips 20k регистраций в секунду делает и при том не особенно то и напрягается. Имхо совершенно кривая схема для sbc. Opensips должен быть настрон как маршрутизатор и перерабатывать пакеты на пути к медиаейетвею. А b2b logic – это функция АТС. Использование rtpproxy итак дает вам сокрытие IP провайдера. А в пакетах легче позаботится простыми функциями типа remove_hf и append_hf. Вот так к примеру:

    remove_hf(“From:”);
    append_hf(“From: $var(from_name) sip:$var(from_phone)@$var(local_domain)>;tag=$ft\r\n”,”CSeq”);

* - обязательные к заполнению поля