OpenSIPS configuration for 2 or more FreeSWITCH installs
About
After 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
Install and configure pre-requisites
Create 'opensips' user
useradd -d /usr/local/etc/opensips -s /sbin/nologin opensips
Create and configure opensips.log
Create the log
touch /var/log/opensips.log
chown opensips:opensips /var/log/opensips.log
Add the log to rsyslog.conf
vi /etc/rsyslog.conf
Add this line to the file:
local0.* /var/log/opensips.log
Restart rsyslog
/etc/init.d/rsyslog restart
Install dependencies
yum install gcc-c++ bison lynx subversion flex curl-devel libxslt libxml2-devel
libxml2 pcre-devel mysql-devel wget make
Download and compile OpenSIPS 1.7.1
Download the source
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
Compile with MySQL support
make all include_modules="db_mysql"
Install OpenSIPS
make include_modules="db_mysql" prefix="/usr/local" install
Configure OpenSIPS
Create the MySQL database
Edit the opensipsctlrc file
vi /usr/local/etc/opensips/opensipsctlrc
Uncomment the following lines:
DBENGINE=MYSQL
DBHOST=localhost
DBNAME=opensips
DBRWUSER=opensips
DBRWPW="opensipsrw"
Run opensipsdbctl
You'll need to enter the MySQL root password when prompted
opensipsdbctl create MySQL password for root:
Create the start-up script
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
Edit opensips.cfg
Create new opensips.cfg
mv /usr/local/etc/opensips/opensips.cfg /usr/local/etc/opensips/opensips.cfg.noload
vi /usr/local/etc/opensips/opensips.cfg
Copy and paste
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:
- With UDP only, everything was working fine, but iPhones registered via TCP/TLS could make calls, but not receive calls
- Call diversion (320 Moved Temporarily) done by registered phones wan't working
So I made the following adjustments to the above script:
- 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
- "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
Start OpenSIPS
/etc/init.d/opensips start
Add FreeSWITCH servers to OpenSIPS dispatcher list
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'
Debugging
tail -f /var/log/opensips.log