Here is the latest version of the Kamailio script, this seems to be working well. What became apparent is that in order for things like conferencing, parking, queuing etc to work, we also need to bring in the DIDs via Kamailio, if not then we have no way of tracking which server they are on. In this version I have achieved this by adding another group to the dispatcher list, this set, set 20, is a list of your carrier IPs.
Please note, for this to work we need to make some ACL changes to Freeswitch and a change to the profile settings.
Please note, in the dispatcher list I have added transport=tcp to my FusionPBX servers, this was necessary on my system before I added code to remove X- headers from the carrier as the max packet size for UDP was being exceeded.
I have left the POSTGRES info in the Kamailio config as I will be bringing this into play in later revisions and when we step up to multiple Kamailio instances.
I've been testing with Digital Ocean and it looks like a couple of droplets can be configured just fine with a shared IP.
Here is the dispatcher.list:
In FusionPBX we need to add a parameter to the internal profile to enable sip proxy acl:
We then need to actually create the proxy acl in Advanced/Access Controls, the CIDR value needs to be the CIDR of your Kamailio instance. Do this for each Kamailio instance that you have.
Once you have done that go into Advanced/Access Controls and add your carrier IPs in the CIDR field of your domain acl list.
At this point I would memcache flush, reloadacl and also restart the internal profile. If in doubt, REBOOT
So not forget that to make changes to profiles work across multiple servers, you need to log into each server and in Advanced/Sip Profiles, edit one variable and then save in order for it to come into play on that server.
Please note, for this to work we need to make some ACL changes to Freeswitch and a change to the profile settings.
Please note, in the dispatcher list I have added transport=tcp to my FusionPBX servers, this was necessary on my system before I added code to remove X- headers from the carrier as the max packet size for UDP was being exceeded.
I have left the POSTGRES info in the Kamailio config as I will be bringing this into play in later revisions and when we step up to multiple Kamailio instances.
I've been testing with Digital Ocean and it looks like a couple of droplets can be configured just fine with a shared IP.
Code:
#!KAMAILIO
#!subst "/MY_PUBLIC_IP_ADDRESS/X.X.X.X/"
#!subst "/POSTGRESQL_IP_ADDRESS/192.168.201.25/"
#!define DBPGURL "postgres://kamailio:dj98dusjj8@POSTGRESQL_IP_ADDRESS:5432/kamailio"
####!define WITH_DEBUG
# - flags
#!define FLAG_FROM_FREESWITCH 1
#!define FLAG_FROM_CARRIER 2
# - defines
#!define DBURL "postgres://kamailio:kamailio@POSTGRESQL_IP_ADDRESS/kamailio"
#!define JANSSON_RPC "conn=presence;addr=localhost;port=8080;priority=10;weight=10"
### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
debug=3
log_stderror=no
memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4
listen=udp:"MY_PUBLIC_IP_ADDRESS":5060
listen=tcp:"MY_PUBLIC_IP_ADDRESS":5060
advertised_address="MY_PUBLIC_IP_ADDRESS"
tcp_connection_lifetime=3605
#enable_tls=yes
####### Modules Section ########
# set paths to location of modules (to sources or installation folders)
mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules"
#loadmodule "db_postgres.so"
loadmodule "mi_fifo.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "mi_rpc.so"
loadmodule "usrloc.so"
loadmodule "regex.so"
loadmodule "registrar.so"
loadmodule "nathelper.so"
#loadmodule "tls.so"
loadmodule "path.so"
#!ifdef WITH_DEBUG
loadmodule "debugger.so"
#!endif
loadmodule "dispatcher.so"
loadmodule "htable.so"
#loadmodule "presence.so"
#loadmodule "presence_dialoginfo.so"
#loadmodule "presence_mwi.so"
#loadmodule "presence_xml.so"
#loadmodule "nsq.so"
#loadmodule "htable.so"
# ----------------- setting module-specific parameters ---------------
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/var/run/kamailio/kamailio_fifo")
modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")
# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)
# ----- nathelper params -----
modparam("nathelper|registrar", "received_avp", "$avp(s:rcv)")
#!ifdef WITH_DEBUG
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
#!endif
#modparam("dispatcher", "db_url",DBPGURL)
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "dst_avp", "$avp(dsdst)")
modparam("dispatcher", "grp_avp", "$avp(dsgrp)")
modparam("dispatcher", "cnt_avp", "$avp(dscnt)")
modparam("dispatcher", "attrs_avp", "$avp(dsattrs)")
modparam("dispatcher", "sock_avp", "$avp(dssocket)")
modparam("dispatcher", "dstid_avp", "$avp(dsdstid)")
modparam("dispatcher", "ds_hash_size", 8)
modparam("dispatcher", "ds_ping_interval", 20)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@dispatcher.local")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=480;code=404")
modparam("dispatcher", "hash_pvar", "$td")
#modparam("presence_dialoginfo", "force_dummy_dialog", 1)
#modparam("presence_xml", "force_dummy_presence", 1)
#modparam("presence_xml", "force_active", 1)
#modparam("presence_xml", "disable_winfo", 1)
#modparam("presence_xml", "disable_bla", 1)
#modparam("htable", "db_url", DBPGURL)
#modparam("htable", "htable", "p=>size=32;autoexpire=3600;")
#modparam("presence", "subs_db_mode", 3)
#modparam("presence", "send_fast_notify", 1)
#modparam("presence", "clean_period", 30)
#modparam("presence", "publ_cache", 0)
#modparam("presence", "min_expires_action", 1)
#modparam("presence", "min_expires", 300)
#modparam("presence", "max_expires", 3600)
#modparam("presence", "sip_uri_match", 1)
#modparam("presence", "waitn_time", 1)
#modparam("presence", "notifier_processes", 1)
#modparam("presence", "force_delete", 1)
#modparam("presence", "db_url", DBPGURL)
#modparam("presence", "active_watchers_table", "active_watchers")
####### Routing Logic ########
# Main SIP request routing logic
# - processing of any incoming SIP request starts with this route
# - note: this is the same as route { ... }
request_route {
xlog("L_INFO", "$ci|log|SIP message [$rm] from $si:$sp");
if (uri=="sip:149.202.190.100:5777") {
if ((method==OPTIONS) && (! uri=~"sip:.*[@]+.*")) {
options_reply();
}
}
# per request initial checks
route(REQINIT);
# CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
route(CHECK_SOURCE_IP);
# handle requests within SIP dialogs
route(WITHINDLG);
###############################
### HANDLE INITIAL REQUESTS ###
# handle retransmissions
if(t_precheck_trans()) {
t_check_trans();
exit;
}
t_check_trans();
if (is_method("INVITE|REFER|SUBSCRIBE")) {
record_route();
}
if (is_method("NOTIFY") && $hdr(event) == "check-sync" && isflagset(FLAG_FROM_FREESWITCH)) {
record_route();
xlog("L_INFO", "$ci|log|Rebooting phone [$ru]\n");
t_on_reply("REPLY_FROM_DEVICE"); # handle NAT
route(RELAY);
}
if (!isflagset(FLAG_FROM_FREESWITCH) && is_method("REGISTER")) {
add_path();
}
# handle INVITEs
route(DISPATCH);
route(RELAY);
}
# Per SIP request initial checks
route[REQINIT] {
if (!mf_process_maxfwd_header("10")) {
#xlog("L_WARN", "$ci|end|too much hops, not enough barley");
send_reply("483", "Too Many Hops");
exit;
}
if (!sanity_check()) {
#xlog("L_WARN", "$ci|end|message is insane");
exit;
}
if ($ua == "friendly-scanner" || $ua == "sundayddr" || $ua =~ "sipcli" ) {
#xlog("L_WARN", "$ci|end|dropping message with user-agent $ua");
exit;
}
if (is_method("PUBLISH")) {
xlog("L_WARN", "$ci|end|dropping PUBLISH messages for now $ua");
exit;
}
}
route[CHECK_SOURCE_IP] {
if (ds_is_from_list("1","3")) {
setflag(FLAG_FROM_FREESWITCH);
} else {
route(NAT_TEST_AND_CORRECT);
}
}
route[DISPATCH] {
if (ds_is_from_list("20","3")){
setflag(FLAG_FROM_CARRIER);
xlog("L_NOTICE", "FLAG_FROM_CARRIER SET!");
# strip all headers whose names match "Subject", contain "P-" or "X-".
remove_hf_re("Subject|P-.*|X-.*");
append_hf("X-Auth-IP: $si\r\n");
if (!ds_select_dst("1", "7")) {
#if we are here that means no destination is available. We notify the user by 404 and exit the script.
xlog("L_NOTICE", "No destination available!");
send_reply("404", "No destination");
exit;
}
}
if (isflagset(FLAG_FROM_FREESWITCH)) {
xlog("L_NOTICE", "FLAG_FROM_FREESWITCH IS SET!");
t_on_reply("REPLY_FROM_DEVICE"); # handle NAT
} else if (!ds_select_dst("1", "7")) {
#if we are here that means no destination is available. We notify the user by 404 and exit the script.
xlog("L_NOTICE", "No destination available!");
send_reply("404", "No destination");
exit;
}
}
route[RELAY] {
if (is_method("INVITE")) {
if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
}
if (!t_relay()) {
sl_reply_error();
}
exit;
}
onreply_route[REPLY_FROM_DEVICE] {
route(NAT_TEST_AND_CORRECT);
}
# manage failure routing cases
failure_route[MANAGE_FAILURE] {
if (t_is_canceled()) {
exit;
}
}
route[NAT_TEST_AND_CORRECT] {
if (is_method("REGISTER")) {
if (nat_uac_test("19")) {
fix_nated_contact();
force_rport();
}
} else {
if (nat_uac_test("3")) {
fix_nated_contact();
force_rport();
}
if (has_body("application/sdp") && nat_uac_test("8")) {
fix_nated_sdp("10");
}
}
}
# Handle requests within SIP dialogs
route[WITHINDLG] {
if (has_totag()) {
if (is_method("INVITE|UPDATE|NOTIFY")) { # fix reply from UPDATE or NOTIFY (in-dialog)
t_on_reply("REPLY_FROM_DEVICE"); # handle NAT
}
# sequential request withing a dialog should
# take the path determined by record-routing
if (loose_route()) {
route(RELAY);
} else {
if (is_method("NOTIFY")) {
route(RELAY);
}
if (is_method("SUBSCRIBE") && uri == myself) {
# in-dialog subscribe requests
route(PRESENCE);
exit;
}
if (is_method("ACK")) {
if (t_check_trans()) {
# no loose-route, but stateful ACK;
# must be an ACK after a 487
# or e.g. 404 from upstream server
t_relay();
exit;
} else {
# ACK without matching transaction ... ignore and discard
#xlog("ACK without matching transaction ... ignore and discard");
exit;
}
}
sl_send_reply("404","Not here");
}
exit;
}
}
####### Presence Routes #######
route[PRESENCE] {
if(!is_method("PUBLISH|SUBSCRIBE"))
return;
route(TOFUSIONPBX);
exit;
}
# Send to FusionPBX
route[TOFUSIONPBX] {
route(DISPATCH);
route(RELAY);
exit;
}
Here is the dispatcher.list:
Code:
1 sip:37.187.X.X:5060;transport=tcp
1 sip:176.31.X.X:5060;transport=tcp
20 sip:178.22.136.19:5060 4
20 sip:178.22.136.34:5060 4
20 sip:178.22.140.34:5060 4
20 sip:178.22.140.35:5060 4
20 sip:178.22.143.66:5060 4
20 sip:178.22.143.69:5060 4
In FusionPBX we need to add a parameter to the internal profile to enable sip proxy acl:
We then need to actually create the proxy acl in Advanced/Access Controls, the CIDR value needs to be the CIDR of your Kamailio instance. Do this for each Kamailio instance that you have.
Once you have done that go into Advanced/Access Controls and add your carrier IPs in the CIDR field of your domain acl list.
At this point I would memcache flush, reloadacl and also restart the internal profile. If in doubt, REBOOT
So not forget that to make changes to profiles work across multiple servers, you need to log into each server and in Advanced/Sip Profiles, edit one variable and then save in order for it to come into play on that server.