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

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. В упрощенном виде, конфигурации привожу здесь.
В конфигурации:
8.8.8.8/192.168.0.1 — балансировщик
192.168.0.2 — postgresql балансировщика
192.168.0.3 — class5 softswitch
8.8.4.4/192.168.0.100 — нода кластера
192.168.0.101 — postgresql ноды
192.168.0.200 — rtpproxy
В ближайших планах ввод в эксплуатацию еще одного такого же кластера. Оба кластера будут разнесены географически, между ними трафик будет распределяться с помощью Round-Robbin DNS (SRV-записи).
А также:
— задействовать cachedb для кэширования
— добавить функционал для автоматической блокировки сканирующих ботов и несложных атак
— добавить функционал для блокировки перебиральщиков паролей
Идея заключалась в создании максимально простого балансировщика, который будет распределять звонки между нодами кластера. Обработку звонков планировалось производить полностью на нодах.
Одновременно с этим, возникла идея о скрытии топологии. В opensips для этого есть два механизма.
Первый – недавно появившаяся функция topology_hiding() в модуле dialog.
Второй – модуль b2b_logic, реализующий полноценный b2b.
После испытаний, было принято окончательное решение использовать b2b_logic. Однако, мы столкнулись с ограничениями в его функциональности. Например, не было возможности полноценно работать с sip-заголовками (удалять, добавлять, изменять), а также не было режима прозрачной аутентификации. Аутентификация была возможна на сервере с b2b, но пробрасывать ее на следующий сервер b2b не умел. Были написаны соответствующие патчи к основному коду, которые разработчики opensips обещают включить в релиз 1.9.
Также следует отметить, что opensips построен так, что на одном запущенном экземпляре нельзя использовать b2b и прокси одновременно. В итоге, родилась схема, которая стала для нас рабочей.

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=$proto\r\n"); 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)\r\n"); 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: $du\r\n", "From"); remove_hf("Call-ID"); append_hf("Call-ID: $(ci{s.md5})\r\n", "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 softswitch (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=$proto\r\n"); } 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 softswitch append_hf("x-src-uri: sip:$si:$sp;transport=$proto\r\n"); } 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 softswitch append_hf("x-src-uri: sip:$si:$sp;transport=$proto\r\n"); } 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 softswitch 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 softswitch # Fix via in OPTIONS ping reply if ( (is_method("OPTIONS")) && ($fu == "sip:pinger@cool.sip") ) insert_hf("Via: SIP/2.0/UDP $si:$sp\r\n"); } 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$mb\n"); if ($hdr(x-orig-to)) { # Restore original To header after b2b remove_hf("To"); append_hf("To: $hdr(x-orig-to)\r\n", "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$mb\n"); 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:yes\r\n") ######################################################################## 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:yes\r\n") 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)\r\n"); 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_SOFTSWITCH)) { # Softswitch -> SIP xlog("L_INFO", "[INVITE] Call from softswitch (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; 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_SOFTSWITCH] { 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_SOFTSWITCH] { 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_SOFTSWITCH)) $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)\n\n"); $var(dest_uri) = "sip:"+$dd+":"+$dp+";transport="+$dP; append_hf("Route: <sip:$var(balancer_ip);lr;received=$var(dest_uri)>\r\n", "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_SOFTSWITCH)) { 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$mb\n"); 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"); }
В конфигурации:
8.8.8.8/192.168.0.1 — балансировщик
192.168.0.2 — postgresql балансировщика
192.168.0.3 — class5 softswitch
8.8.4.4/192.168.0.100 — нода кластера
192.168.0.101 — postgresql ноды
192.168.0.200 — rtpproxy
В ближайших планах ввод в эксплуатацию еще одного такого же кластера. Оба кластера будут разнесены географически, между ними трафик будет распределяться с помощью Round-Robbin DNS (SRV-записи).
А также:
— задействовать cachedb для кэширования
— добавить функционал для автоматической блокировки сканирующих ботов и несложных атак
— добавить функционал для блокировки перебиральщиков паролей
