https://wiki.freeswitch.org/wiki/OpenSIPS_configuration_for_2_or_more_FreeSWITCH_installs
fter much searching and experimentation, I've found an opensips.cfg that distributes calls to two or more FreeSWITCH boxes. This guide assumes you have a MySQL server setup on the same machine you are installing. Special thanks to the guys at 2600hz for this opensips.cfg
Please note that you should add this line to sofia.conf in FreeSWITCH for presence: <param name="outbound-proxy" value="ext.ip.of.opensips"/>
TODO: Fix script to send all "parking" and "conference" calls to a single FS server.
Tested on CentOS 6.X with OpenSIPS 1.7.1
Contents[hide] |
useradd -d /usr/local/etc/opensips -s /sbin/nologin opensips
touch /var/log/opensips.log chown opensips:opensips /var/log/opensips.log
vi /etc/rsyslog.conf
Add this line to the file:
local0.* /var/log/opensips.log
/etc/init.d/rsyslog restart
yum install gcc-c++ bison lynx subversion flex curl-devel libxslt libxml2-devel libxml2 pcre-devel mysql-devel wget make
cd /usr/src wget http://opensips.org/pub/opensips/1.7.1/src/opensips-1.7.1_src.tar.gz tar zxvf opensips-1.7.1_src.tar.gz cd opensips-1.7.1-tls
make all include_modules="db_mysql"
make include_modules="db_mysql" prefix="/usr/local" install
vi /usr/local/etc/opensips/opensipsctlrc
Uncomment the following lines:
DBENGINE=MYSQL DBHOST=localhost DBNAME=opensips DBRWUSER=opensips DBRWPW="opensipsrw"
You'll need to enter the MySQL root password when prompted
opensipsdbctl create MySQL password for root:
cp /usr/src/opensips-1.7.1-tls/packaging/rpm/opensips.init /etc/init.d/opensips sed -i "s/\/usr\/sbin\/opensips/\/usr\/local\/sbin\/opensips/g" /etc/init.d/opensips sed -i "s/\/etc\/opensips/\/usr\/local\/etc\/opensips/g" /etc/init.d/opensips sed -i "s/\/etc\/default\/opensips/\/usr\/local\/etc\/opensips/g" /etc/init.d/opensips sed -i "s/RUN_OPENSIPS=no/RUN_OPENSIPS=yes/g" /etc/init.d/opensips chmod +x /etc/init.d/opensips chkconfig opensips on
mv /usr/local/etc/opensips/opensips.cfg /usr/local/etc/opensips/opensips.cfg.noload vi /usr/local/etc/opensips/opensips.cfg
Make sure you replace "ext.ip.addr" with the public IP address of your OpenSIPS server.
###################################################################### ## Core Parameters ###################################################################### # chroot= # group="opensips" # user="opensips" # dbversion_table= disable_core_dump=no max_while_loops=100 maxbuffer=262144 memdump=3 memlog=2 # open_files_limit=2048 server_signature=no server_header="Server: OpenSIPS" user_agent_header="User-Agent: OpenSIPS" ###################################################################### ## Core Fork Parameters ###################################################################### fork=yes children=8 tcp_children=8 ###################################################################### ## Core Logging Parameters ###################################################################### debug=3 sip_warning=0 log_stderror=no log_facility=LOG_LOCAL0 log_name="opensips" ###################################################################### ## Aliases ###################################################################### auto_aliases=yes alias=localhost alias=localhost.localdomain ###################################################################### ## Connectivity ###################################################################### #listen=udp:eth0:5060 listen=udp:eth0:5060 listen=tcp:eth0:5060 listen=udp:eth0:7000 listen=tcp:eth0:7000 #listen=udp:eth0:7000 #listen=tcp:eth0:7000 # listen=udp:eth1:5060 tos=IPTOS_LOWDELAY # advertised_address=174.129.131.38 # advertised_port=5060 mcast_loopback=no mcast_ttl=1 mhomed=0 # tcp_accept_aliases tcp_connect_timeout=3 tcp_connection_lifetime=120 tcp_max_connections=2048 # tcp_poll_method=select ###################################################################### ## DNS ###################################################################### dns=no dns_retr_time=1 dns_retr_no=3 # dns_servers_no=2 dns_try_ipv6=no disable_dns_blacklist=yes disable_dns_failover=no dns_use_search_list=no rev_dns=no ###################################################################### ## SIP ###################################################################### check_via=0 #! disable_503_translation=no disable_stateless_fwd=no disable_tcp=no # disable_tls=no #! reply_to_via=1 ###################################################################### ## TLS ###################################################################### # disable_tls=no # listen=tls:your_IP:5061 # tls_verify_server=1 # tls_verify_client=1 # tls_require_client_certificate=0 # tls_method=TLSv1 # tls_certificate="/usr/local/etc/opensips/tls/user/user-cert.pem" # tls_private_key="/usr/local/etc/opensips/tls/user/user-privkey.pem" # tls_ca_list="/usr/local/etc/opensips/tls/user/user-calist.pem" ###################################################################### ## Destination Blacklist ###################################################################### # dst_blacklist=gw:{( tcp , 192.168.2.100 , 5060 , "" ),( any , 192.168.2.101 , 0 , "" )} # dst_blacklist=net_filter2:{ !( any , 192.168.30.0/255.255.255.0 , 0 , "" )} ###################################################################### ## Attribute Value Pairs ###################################################################### # avp_aliases="uuid=I:660;email=s:email_addr;fwd=i:753" ###################################################################### ## Module Loading ###################################################################### mpath="/usr/local/lib64/opensips/modules/" loadmodule "db_mysql.so" loadmodule "localcache.so" loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "dialog.so" loadmodule "maxfwd.so" loadmodule "rr.so" loadmodule "path.so" loadmodule "uri.so" loadmodule "textops.so" loadmodule "usrloc.so" loadmodule "nathelper.so" loadmodule "nat_traversal.so" loadmodule "uac_redirect.so" loadmodule "dispatcher.so" loadmodule "mi_fifo.so" # loadmodule "mi_datagram.so" ###################################################################### ## Localcache Module Parameters ###################################################################### modparam("localcache", "cache_table_size", 10) modparam("localcache", "cache_clean_period", 120) ###################################################################### ## Stateless UA Module Parameters ###################################################################### modparam("sl", "enable_stats", 1) ###################################################################### ## SIP Transaction UA Module Parameters ###################################################################### modparam("tm", "fr_timer", 2) 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", "fr_timer_avp", "$avp(final_reply_timer)") # modparam("tm", "fr_inv_timer_avp", "$avp(25)") # modparam("tm", "tw_append", # "test: ua=$hdr(User-Agent) ;avp=$avp(i:10);$rb;time=$Ts") modparam("tm", "pass_provisional_replies", 1) # modparam("tm", "syn_branch", 1) # modparam("tm", "onreply_avp_mode", 0) # modparam("tm", "disable_6xx_block", 0) # modparam("tm", "enable_stats", 1) # modparam("tm", "minor_branch_flag", 3) ###################################################################### ## Max Forward Module Parameters ###################################################################### modparam("maxfwd", "max_limit", 30) ###################################################################### ## Record Route Module Parameters ###################################################################### #modparam("rr", "enable_full_lr", 1) modparam("rr", "append_fromtag", 1) modparam("rr", "enable_double_rr", 0) modparam("rr", "add_username", 0) ###################################################################### ## Path Module Parameters ###################################################################### modparam("path", "use_received", 1) ###################################################################### ## URI Module Parameters ###################################################################### # modparam("uri", "aaa_url", "radius:/etc/radiusclient-ng/radiusclient.conf") modparam("uri", "use_sip_uri_host", 0) modparam("uri", "use_uri_table", 0) modparam("uri", "service_type", 10) modparam("uri", "use_domain", 1) modparam("uri", "use_uri_table", 0) # modparam("uri", "db_url", "mysql://username:password@localhost/opensips") # modparam("uri", "db_table", "uri") # modparam("uri", "user_column", "username") # modparam("uri", "domain_column", "domain") # modparam("uri", "uriuser_column", "uri_user") ###################################################################### ## User Location Module Parameters ###################################################################### modparam("usrloc", "nat_bflag", 6) modparam("usrloc", "use_domain", 1) modparam("usrloc", "desc_time_order", 0) modparam("usrloc", "timer_interval", 60) modparam("usrloc", "matching_mode", 0) modparam("usrloc", "cseq_delay", 20) modparam("usrloc", "hash_size", 9) modparam("usrloc", "db_mode", 0) # modparam("usrloc", "db_url", "dbdriver://username:password@dbhost/dbname") #modparam("usrloc", "fetch_rows", 2000) 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") ###################################################################### ## Nathelper Module Parameters ###################################################################### # modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:7890") # modparam("nathelper", "natping_interval", 30) # modparam("nathelper", "ping_nated_only", 1) # modparam("nathelper", "natping_processes", 3) # modparam("nathelper", "sipping_bflag", 7) # modparam("nathelper", "sipping_from", "sip:sipcheck@184.106.157.174") # modparam("nathelper", "sipping_method", "INFO") ###################################################################### ## NAT Traversal Module Parameters ###################################################################### modparam("nat_traversal", "keepalive_interval", 60) modparam("nat_traversal", "keepalive_method", "OPTIONS") modparam("nat_traversal", "keepalive_from", "sip:keepalive@ext.ip.addr:5060") modparam("nat_traversal", "keepalive_state_file", "/tmp/opensips_keepalive_state") ###################################################################### ## UAC Redirect Module Parameters ###################################################################### modparam("uac_redirect", "default_filter", "accept") # modparam("uac_redirect", "deny_filter", NULL) # modparam("uac_redirect", "accept_filter", NULL) # modparam("uac_redirect", "acc_function", "acc_log_request") # modparam("uac_redirect", "acc_db_table", "acc") ###################################################################### ## Dispatcher Module Parameters ###################################################################### #modparam("dispatcher", "list_file", "/etc/opensips/dispatcher.list") modparam("dispatcher", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("dispatcher", "flags", 2) modparam("dispatcher", "use_default", 0) modparam("dispatcher", "force_dst", 1) modparam("dispatcher", "dst_avp", "$avp(271)") modparam("dispatcher", "attrs_avp", "$avp(272)") modparam("dispatcher", "grp_avp", "$avp(273)") modparam("dispatcher", "cnt_avp", "$avp(274)") modparam("dispatcher", "hash_pvar", "$avp(273)") # modparam("dispatcher", "setid_pvar", "$var(setid)") modparam("dispatcher", "ds_ping_method", "OPTIONS") modparam("dispatcher", "ds_ping_from", "sip:sipcheck@ext.ip.addr:5060") modparam("dispatcher", "ds_ping_interval", 10) # modparam("dispatcher", "ds_ping_sock", "udp:ext.ip.addr:5060") modparam("dispatcher", "ds_probing_threshhold", 3) modparam("dispatcher", "ds_probing_mode", 1) modparam("dispatcher", "options_reply_codes", "501,403,404,400,200") ###################################################################### ## MI-FIFO Module Parameters ###################################################################### modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") ###################################################################### ## MI-Datagram Module Parameters ###################################################################### # modparam("mi_datagram", "socket_name", "udp:127.0.0.1:8889") # modparam("mi_datagram", "children_count", 1) # modparam("mi_datagram", "unix_socket_mode", 0600) # modparam("mi_datagram", "unix_socket_group", "root") # modparam("mi_datagram", "unix_socket_user", "root") # modparam("mi_datagram", "socket_timeout", 2000) # modparam("mi_datagram", "reply_indent", "\t") ###################################################################### ## XLog Module Parameters ###################################################################### # modparam("xlog", "buf_size", 4096) # modparam("xlog", "force_color", 0) ###################################################################### ## Multiple Module Parameters ###################################################################### ###################################################################### ## Main Request Routing ###################################################################### route { # log the basic info regarding this call xlog("L_INFO", "$ci|start|recieved $oP request $rm $ou"); xlog("L_INFO", "$ci|log|source $si:$sp"); xlog("L_INFO", "$ci|log|from $fu"); xlog("L_INFO", "$ci|log|to $tu"); # check that hop cound for this request and make sure it is under 10 # to prevent endless loops if (!mf_process_maxfwd_header("10")) { xlog("L_WARN", "$ci|end|to many hops"); sl_send_reply("483", "We refuse to process this endless imbroglio"); exit; } # this check detemines if the opensips has routed the request to itself, # this happens because the server is the destination of the request but # we mangle it to send it else where. When that mangeling fails and we # still relay it then it just comes right back to us... if (src_ip==myself) { xlog("L_WARN", "$ci|end|sourced from this server"); exit; } # currently we dont support subscribe so to keep the noise down # just end the request here. For options just end the request here as well. if (is_method("OPTIONS")) { xlog("L_NOTICE", "$ci|end|unsupported method"); sl_send_reply("503", "Rawr!!"); exit; } # if the source IP/port are in one of the server dispatch lists # then this request originated from one of our media servers, mark it # as such by setting flag 26 if (ds_is_in_list("$si", "", "1")) { xlog("L_INFO", "$ci|log|originated from internal sources"); # Flag 26 marks the source as a on-net server setflag(26); setbflag(26); } # if the request source IP/port was not in any dispatcher lists # this this originated outside our equipment (carrier, client, ect) else { xlog("L_INFO", "$ci|log|originated from external sources"); } # if the to header has a tag attached then it implies this request # has been processed by us before (IE: a media server has added # its tag on the to header in prior messages) if (has_totag()) { # sequential request within a dialog should # take the path determined by record-routing if (loose_route()) { append_hf("P-hint: rr-enforced\r\n"); # if we have locked this call to a media server then # maintain that association if (cache_fetch("local", "$ci", $avp(55))) { if (is_method("BYE")) { # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|log|cleaned up call id from cache"); } else if (isflagset(26)) { cache_store("local", "$tU", "$avp(55)", 3600); xlog("L_INFO", "$ci|log|maintaining associated $tU with media server $avp(55)"); } else if ($ct.fields(uri)) { cache_store("local", "$(ct.fields(uri){uri.user})", "$avp(55)", 3600); xlog("L_INFO", "$ci|log|maintaining associated $(ct.fields(uri){uri.user}) with media server $avp(55)"); } cache_store("local", "$ci", "$avp(55)", 3600); } xlog("L_INFO", "$ci|log|forwarding based on the route set"); if (isflagset(26)) { route(internal_to_external_relay); } else { route(external_to_internal_relay); } exit(); } else if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server xlog("L_INFO", "$ci|log|in dialog request belongs to a known transaction"); route(logged_relay); } else { # ACK without matching transaction -> # ignore and discard xlog("L_NOTICE", "$ci|end|no matching transaction"); } exit(); } else if ( is_method("NOTIFY") ) { route(logged_relay); exit(); } # request with a to tag that cant be routed loosly and is not an ACK # ignor eand discard xlog("L_WARN", "$ci|end|could not route in dialog"); sl_send_reply("486", "PC Load Letter"); exit(); } # if the request is to cancel a transaction process it now if (is_method("CANCEL")) { # If this cancel is part of a transaction # then pass it along to concerned parties if (t_check_trans()) { xlog("L_INFO", "$ci|log|request belogs to a known transaction"); route(logged_relay); } # if the cancel does not belong to a known transaction or a # request that has not progressed outside this server dont relay it else { xlog("L_NOTICE", "$ci|end|no matching transaction"); } # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|log|cleaned up call id from cache"); exit; } # If this is a retransmission it will break/stop the script # and do standard processing of the message t_check_trans(); # Except for an ACK no request should have a route set with no to tag, this would # indicate that the intial request has the Route headers and is likely someone trying # to get us to send the request were they want if (loose_route()) { if (!is_method("ACK")) { xlog("L_WARN", "$ci|end|initial request contained a preloaded route set"); sl_send_reply("403", "The only winning move it not to play"); exit; } } # If the request is a register we will pass it along but we need # to add the path header (along with the received IP/port info) if (is_method("REGISTER")) { # if we fail to add the path header then dont let it # register because it will cause issues later... if (!add_path_received()) { xlog("L_ERR", "$ci|log|unable to add path"); sl_send_reply("503", "Internal path befuddlement"); # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|end|cleaned up call id from cache"); exit; } xlog("L_INFO", "$ci|log|added path"); } # for all initial request (not having been processed above in the has_totag) # that are not a register or message add this sever to the route set on the # request so subsequent messages come through this server if (!is_method("REGISTER|MESSAGE")) { # Record the route that this request has taken # so we remain in the signaling path record_route(); xlog("L_INFO", "$ci|log|added this server to the route set"); } # if the request is from a media server send it out if (isflagset(26)) { route(internal_to_external_relay); exit(); } # if the request is not from a media server it must be for one, # there is much work to do! # load a list of currently active media servers # if no media server could be set with ds_select_domain then there are no # active servers, no need to conitnue if (!ds_select_domain("1", "4")) { xlog("L_ERR", "$ci|end|no servers avaliable"); sl_send_reply("480", "The cake is a lie!"); exit; } if (cache_fetch("local", "$ou", $avp(55))) { xlog("L_INFO", "$ci|log|request $ou is associated with media server $avp(55)"); cache_remove("local", "$ou"); cache_store("local", "$ci", "$avp(55)", 3600); } # if the request is not from our media severs but has a call-id in localcache # then change the routing to go to the server previously associated with it. else if (cache_fetch("local", "$ci", $avp(55))) { cache_store("local", "$ci", "$avp(55)", 3600); xlog("L_INFO", "$ci|log|call-id is associated with media server $avp(55)"); } # if the request is not from our media severs but has a contact uri in localcache # then change the routing to go to the server previously associated with it. else if ($ct.fields(uri) && cache_fetch("local", "$(ct.fields(uri){uri.user})", $avp(55))) { cache_store("local", "$(ct.fields(uri){uri.user})", "$avp(55)", 3600); xlog("L_INFO", "$ci|log|contact $(ct.fields(uri){uri.user}) is associated with media server $avp(55)"); } # if the request is not from our media servers and no associations in localcache # then use the distribute list as is else { xlog("L_INFO", "$ci|log|routing call to arbitrary media server $rd:$rp"); } # if the dispatcher list (in 271) does not start with # the request domain/port that we are sending this call # to, re-order the list so that it does if($avp(55) && $(avp(271)[0]) != $avp(55)) { # create a index var for our loop (arrays are start at 0 and this is a count) $var(i) = $avp(274) - 1; # loop over the dispatcher list while($var(i) > 0) { # if this element in the dispatch list is the same # as the call destination if($(avp(271)[$var(i)]) == $avp(55)) { # replace it with the first element of the list $(avp(271)[$(var(i))]) = $(avp(271)[0]); # break out of the loop $var(i) = -1; } $var(i) = $var(i) - 1; } # handles the case were we only have two servers # and the one that we are locked to has failed if ($var(i) >= 0) { xlog("L_INFO", "$ci|log|associated media server is inactive, moving to $rd"); if ($ct.fields(uri) && cache_fetch("local", "$ci", $avp(56))) { cache_store("local", "$(ct.fields(uri){uri.user})", "sip:$rd:$rp", 3600); xlog("L_INFO", "$ci|log|associated contact $(ct.fields(uri){uri.user}) with media server sip:$rd:$rp"); } # update the callid cache cache_store("local", "$ci", "sip:$rd:$rp", 3600); } # the server we are locked to is in the active server list from then # dispatcher so re-arrange the list to try it first else { xlog("L_INFO", "$ci|log|re-ordering the dispatcher list to keep associated server first"); # set the first element of the list to the destination $(avp(271)[0]) = $avp(55); # set the domain for this request (server IP to route to) $rd = $(avp(55){uri.host}); # set the port for this request (server IP to route to) $rp = $(avp(55){uri.port}); } } route(external_to_internal_relay); } route[external_to_internal_relay] { # 1. correct any nat issues # 2. remove any X-AUTH-IP headers so we will be the only one to set it # 3. set the X-AUTH-IP header for freeswitch ACLs # 4. set the final reply timer to two seconds, so we failover faster # 5. arm a logging branch for replies # 6. arm a failure branch that will try another one of our media servers when possible route("nat_test_and_correct"); remove_hf("X-AUTH-IP"); append_hf("X-AUTH-IP: $si\r\n"); xlog("L_INFO", "$ci|log|X-AUTH-IP: $si"); $avp(final_reply_timer) = 2; t_on_reply("internal_reply"); t_on_failure("internal_fault"); route("logged_relay"); exit; } route[internal_to_external_relay] { # if the request is from a media server then assume it is going somewhere # outside our control and give that equipment longer to respond. # Also arm a branch to log the replies $avp(final_reply_timer) = 6; t_on_reply("external_reply"); route("logged_relay"); exit; } route[logged_relay] { # try to send the request on its way, if it fails send back a # stateless error to the requestor if (t_relay()) { xlog("L_INFO", "$ci|pass|$rd:$rp"); } else { xlog("L_ERR", "$ci|end|unable to relay message"); sl_reply_error(); } } route[nat_test_and_correct] { # 1. Contact header field is searched for occurrence of RFC1918 addresses # if (nat_uac_test("1")) # { # xlog("L_INFO", "$ci|log|contact header field contains a RFC1918 address"); # # fix_contact(); # } # 2 - the "received" test is used: address in Via is compared against source IP address of signaling if (nat_uac_test("2")) { xlog("L_INFO", "$ci|log|address in Via differs from source IP"); # adds the rport parameter to the first Via header force_rport(); fix_contact(); } # if the request has a body see if it needs NAT corrections as well, # this check looks at: # 8. SDP is searched for occurrence of RFC1918 addresses if (has_body("application/sdp") && nat_uac_test("8")) { xlog("L_INFO", "$ci|log|SDP contains a RFC1918 address"); # alters the SDP information in order to facilitate NAT traversal. # 2. rewrite media IP address (c=) with source IP # 8. rewrite IP from origin description (o=) with source IP fix_nated_sdp("10"); } } onreply_route[external_reply] { # this branch handles replies that are comming from equipment # outside our control xlog("L_INFO", "$ci|start|recieved external reply $rs $rr"); xlog("L_INFO", "$ci|log|source $si:$sp"); # This ensures that if a endpoint recieves a call they can properly # transfer that call # TODO: this will track calls made to carriers when we start sending carrier # traffic through opensips # Target: A endpoint answering a call made from one of our media # servers should lock that endpoint to the server if (t_check_status("200") && is_method("INVITE") && $(fd{ip.isip}) && ds_is_in_list("$fd", "", "1")) { $var(d) = $(fu{uri.host}); if ($(fu{uri.port}) == 0) { $var(p) = 5060; } else { $var(p) = $(fu{uri.port}); } if ($ct.fields(uri)) { cache_store("local", "$(ct.fields(uri){uri.user})", "sip:$var(d):$var(p)", 3600); xlog("L_INFO", "$ci|log|associated $(ct.fields(uri){uri.user}) with media server sip:$var(d):$var(p)"); } cache_store("local", "$ci", "sip:$var(d):$var(p)", 3600); xlog("L_INFO", "$ci|log|associated call-id with media server sip:$var(d):$var(p)"); } if (is_method("BYE")) { # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|log|cleaned up call id from cache"); } route("nat_test_and_correct"); xlog("L_INFO", "$ci|pass|$(<request>si):$(<request>sp)"); # if the reply is not dropped (only provisional replies can be), # it will be injected and processed by the transaction engine. } onreply_route[internal_reply] { # this branch handles replies that are comming from our media server if(t_local_replied("last")) { xlog("L_INFO", "$ci|start|recieved local internal reply $T_reply_code $rr"); } else { xlog("L_INFO", "$ci|start|recieved internal reply $T_reply_code $rr"); xlog("L_INFO", "$ci|log|source $si:$sp"); } # Ensure that if we challenge an endpoint its response is not round-robin'd # We have to do it in the reply so we have the correct call id # Target: Endpoint intiated a request that was challenged, lock that # call id to the challenging server so it recieves the reply if (t_check_status("(407)|(401)") && $(si{ip.isip}) && ds_is_in_list("$si", "", "1")) { cache_store("local", "$ci", "sip:$si:$sp", 3600); xlog("L_INFO", "$ci|log|associated call-id with media server sip:$si:$sp"); } if (is_method("BYE")) { # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_store("local", "$ci", "sip:$si:$sp", 360); xlog("L_INFO", "$ci|log|cleaned up call id from cache"); } if ($rs < 300) { xlog("L_INFO", "$ci|pass|$(<request>si):$(<request>sp)"); } # if the reply is not dropped (only provisional replies can be), # it will be injected and processed by the transaction engine. } failure_route[internal_fault] { # this branch handles failures (>=300) to our media servers, # which we can sometimes overcome by routing to another server # if the failure cause was due to the transaction being # cancelled then we are complete if (t_was_cancelled()) { xlog("L_INFO", "$ci|log|transaction was cancelled"); # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|end|cleaned up call id from cache"); exit; } # if the failure case was soemthing that we should recover # from then try to find a new media server if (t_check_status("(401)|(407)|(403)")) { xlog("L_INFO", "$ci|log|failure route ignoring auth reply $T_reply_code $rr"); } else if (t_check_status("402")) { send_reply("486", "More money please"); exit(); } else if (t_check_status("(4[0-9][0-9])|(5[0-9][0-9])")) { xlog("L_INFO", "$ci|start|received failure reply $T_reply_code $rr"); if (cache_fetch("local", "$ci-failure", $avp(55))) { $avp(55) = $(avp(55){s.int}); } else { $avp(55) = 0; } xlog("L_INFO", "$ci|log|attempting retry $avp(55) of failed request"); # try to find a new media server to send the call to if($avp(55) < 3 && ds_next_domain()) { xlog("L_INFO", "$ci|log|routing call to next media server $rd:$rp"); # store the new callid association cache_store("local", "$ci", "sip:$rd:$rp", 3600); # if the request has a contact and is an INVITE then store the new # association if ($ct.fields(uri) && is_method("INVITE")) { cache_store("local", "$(ct.fields(uri){uri.user})", "sip:$rd:$rp", 3600); xlog("L_INFO", "$ci|log|associated contact $(ct.fields(uri){uri.user}) with media server sip:$rd:$rp"); } # reset the final reply timer $avp(final_reply_timer) = 2; t_on_reply("internal_reply"); t_on_failure("internal_fault"); xlog("L_INFO", "$ci|pass|$rd:$rp"); # relay the request to the new media server t_relay(); $avp(55) = $avp(55) + 1; cache_store("local", "$ci-failure", "$avp(55)", 60); exit(); } else { cache_remove("local", "$ci-failure"); xlog("L_ERR", "$ci|log|no other media servers avaliable"); } } else if (t_check_status("302")) { if( $(<reply>hdr(X-Redirect-Server)) && $(<reply>ct.fields(uri)) ) { $var(redirect_host) = $(<reply>hdr(X-Redirect-Server){uri.host}); $var(redirect_port) = $(<reply>hdr(X-Redirect-Server){uri.port}); cache_store("local", "$(<reply>ct.fields(uri))", "sip:$var(redirect_host):$var(redirect_port)", 60); xlog("L_INFO", "$ci|log|stored redirect mapping for $(<reply>ct.fields(uri)) to sip:$var(redirect_host):$var(redirect_port)"); xlog("L_INFO", "$ci|log|stored redirect mapping for $(<reply>ct.fields(uri)) to sip:$var(redirect_host):$var(redirect_port)"); remove_hf("X-Redirect-Server"); } } else { xlog("L_INFO", "$ci|log|failure route ignoring reply $T_reply_code $rr"); } if (!t_check_status("(407)|(401)|(302)")) { # remove the association between the call-id and the media server (if one) # but leave the contact user and server to support transfers cache_remove("local", "$ci"); xlog("L_INFO", "$ci|log|cleaned up call id from cache"); } xlog("L_INFO", "$ci|pass|$(<request>si):$(<request>sp)"); # if no new branch is generated or no reply is forced over, by default, # the winning reply will be sent back to UAC. }
A potential correction to the above script:
So I made the following adjustments to the above script:
1) UDP<>TCP not working
modparam("rr", "enable_double_rr", 1) # instead of 0, 1 is default modparam("path", "use_received", 0) # instead of 1, 0 is default
2) "302 Moved temporarily" not working
# in onreply_route[external_reply]: # instead of calling nat_test_and_correct unconditionally, do it only for non 302's : if (!t_check_status("302")) { route("nat_test_and_correct"); }
I'm not absolutely sure, that this won't have any nasty side effects, but at least for me, everything seems to work perfectly fine now. - JJJ
/etc/init.d/opensips start
Replace fs1.ext.ip.addr and fs2.ext.ip.addr with the IP addresses of your FreeSWITCH servers:
opensipsctl dispatcher addgw 1 sip:fs1.ext.ip.addr:5060 0 'fs1' opensipsctl dispatcher addgw 1 sip:fs2.ext.ip.addr:5060 0 'fs2'
tail -f /var/log/opensips.log