Kamailio script to front standard FusionPBX cluster

DigitalDaz

Administrator
Staff member
Here, is a pre-release version of a script to put in front of FusionPBX servers as taught on mcrane's advanced class.

You need to edit the MY_PUBLIC_IP_ADDRESS/X.X.X.X and replace X.X.X.X with your IP address.

The postgres is unused for now but I'll bring that into play soon.

Way down in the dispatcher route, you will see another IP, this is one of my carrier IPs and is just for dealing with some inbound stuff I'm working with. Freeswitch needs amending to work with the inbound part and I'll write that up seperately.

For now, outbound should work fine, just point a domain name to your Kamailio IP.

Much more to come. This version will give per domain balancing, so pretty much all outbound stuff will work. Call transfer, parking, BLF, etc.

You will need to add a dispatcher list to /etc/kamailio, a sample is below along with the script for you to download.

Code:
#!KAMAILIO
#!subst "/MY_PUBLIC_IP_ADDRESS/X.X.X.X/"
#!subst "/POSTGRESQL_IP_ADDRESS/192.168.201.25/"
#!define DBPGURL "postgres://kamailio:Uhh7dk9uefv@POSTGRESQL_IP_ADDRESS:5432/kamailio"
####!define WITH_DEBUG

# - flags
#!define FLAG_FROM_FREESWITCH 1

### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
debug=2
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
listen=udp:"0.0.0.0":5777

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");

    # 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()) {
        setflag(FLAG_FROM_FREESWITCH);
    } else {
        route(NAT_TEST_AND_CORRECT);
    }
}

route[DISPATCH] {
    if(src_ip==178.22.140.34){
        append_hf("X-Auth-IP: $si\r\n");
    }
    if (isflagset(FLAG_FROM_FREESWITCH)) {
        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;
}
You will also need to create a file called /etc/kamailio/dispatcher.list that contains a list of your FusionPBX servers similar to:
Code:
1 sip:37.187.X.X:5060
1 sip:176.31.X.X:5060
1 sip:77.240.X.X:5060
This should also work fine with a single server in the dispatcher list if you just want to have a play with Kamailio.
 

Attached Files:

Last edited: