├── README.md ├── RedisDB-sample.txt ├── basic.cfg ├── call_source.cfg ├── carrier.cfg ├── cdr.cfg ├── channel_limit.cfg ├── kamailio.cfg ├── pstn_dest.cfg ├── redisFiller.txt ├── rtpengine.cfg ├── scripts.txt └── vars.cfg /README.md: -------------------------------------------------------------------------------- 1 | # KamInboundSIP 2 | 3 | KamInboundSIP is an Open Source VoIP Inbound DID Call Routing Solution.This Project will provide the inbound sip using that we can route did call to customer ip or customer Phone number 4 | It is available with the following features 5 | 6 | 1. Inbound Termination with Carrier IP validation. 7 | 2. Carrier LCR for DID/TFN to PSTN forwarding. 8 | 3. Inbound Abuse Block. 9 | 4. CDR in MongoDB. 10 | 5. IPTables Block for Sip-Scanners. 11 | 6. Integration with RtpEngine. 12 | 7. RedisDB for quick DB Access. 13 | 14 | This solution is well tested using SIPP with 50 cps. The backbone of this router is RedisDb which provides quick access to resources for validation of routing content. 15 | It hence reduces the latency and overhead of other sql database engines. The cdr in recorded in MongoDB (nosql database). 16 | 17 | 18 | ### Installation 19 | 20 | ## Step 1: Setup a Redis Instance and build Redis Structure as below : 21 | 22 | ###The DID / TFN number key that holds its information 23 | ``` 24 | HMSET did: custId carrierId countryId billType channel rtpEngine <1/0> routingType 1 25 | ``` 26 | 27 | ###The Carrier IP address as given by the carrier will be stored in a redis set. 28 | ``` 29 | SADD carrierIPAddrs: 30 | ``` 31 | 32 | ###How is the inbound TFN/DID routed 33 | ``` 34 | HMSET direct: dstUri dnis 35 | ``` 36 | eg. HMSET direct: dstUri dnis 37 | 38 | ####The customer in system to whom the DID/TFN belongs 39 | ``` 40 | HMSET customer: routeId balance 700 41 | ``` 42 | 43 | ###The Outbound Carriers will be stored in a Z set ; this gives us control to have priority control. 44 | ``` 45 | ZADD routes::0 46 | ``` 47 | 48 | ###The outbound Carrier IP addresses to route the call out 49 | ``` 50 | SADD carrierOutIps: 51 | ``` 52 | 53 | ###The Rate Card of the carrier as given by the carrier itself; can be a dialcode or entire number (Incase a priority needs to be defined) 54 | ``` 55 | HMSET carrierRates::0 56 | ``` 57 | 58 | ###Inbound Numbers are often abused by callers. A set in Redis corresponding to the TFN/DID will contain such numbers 59 | ``` 60 | SADD abuse: 61 | ``` 62 | 63 | ## Step 2 : Edit the vars.cfg file for your Environment 64 | ``` 65 | #----------------------- Custom Defined Variables ----------------------------# 66 | #!substdef "!MY_IP_ADDR!192.168.2.46!g" 67 | #!substdef "!MY_OB_PORT!5060!g" 68 | #!define DBURL "mongodb://192.168.2.46:27017/kamailio" 69 | #!define REDIS "name=redis;addr=127.0.0.1;port=6379;db=0" 70 | #!define RTPENGINE "udp:192.168.2.46:25061" 71 | #!define MONGOPATH "name=mgs1;uri='mongodb://192.168.2.46:27017/kamailio'" 72 | ``` 73 | 74 | ## Step 3 : Initialise MongoDB Instance 75 | 76 | 1. Create a database with name kamailio 77 | 2. Create collection version and put the below values 78 | 79 | ``` 80 | /* 1 */ 81 | { 82 | "table_name" : "location", 83 | "table_version" : 8 84 | } 85 | 86 | /* 2 */ 87 | { 88 | "table_name" : "dialog", 89 | "table_version" : 7 90 | } 91 | 92 | /* 3 */ 93 | { 94 | "table_name" : "dialog_vars", 95 | "table_version" : 1 96 | } 97 | 98 | ``` 99 | 100 | #### This makes the Proxy Ready for Use 101 | #### Note : if you find any difficulty while using KamInboundSIP please let us know at surendratiwari3@gmail.com 102 | ## We are Working on Web part we will update the frontend repository soon. 103 | -------------------------------------------------------------------------------- /RedisDB-sample.txt: -------------------------------------------------------------------------------- 1 | HMSET did:23418889700 custId 2222 carrierId 1111 countryId 1 billType 1 channel 2 rtpEngine 1 routingType 1 2 | HMSET did:23418889701 custId 2222 carrierId 1111 countryId 1 billType 1 channel 2 rtpEngine 1 routingType 1 3 | SADD carrierIPs:1111 188.244.114.8 4 | HMSET direct:23418889700 dstUri 14703993047 dnis 14703993047 5 | HMSET direct:23418889701 dstUri 14703993047 dnis 14703993047 6 | HMSET customer:2222 routeId 3333 balance 700 7 | ZADD routes:3333:0 1 4444 8 | SADD carrierOutIps:4444 178.62.27.239:5060 9 | HMSET carrierRates:4444:0 14703993047 0.77 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /basic.cfg: -------------------------------------------------------------------------------- 1 | include_file "vars.cfg" 2 | 3 | #-------------------- Modules Section -----------------------------------------# 4 | 5 | loadmodule "kex.so" 6 | loadmodule "corex.so" 7 | 8 | #-------- Loading Mod TM ------------------------------------------------------# 9 | 10 | loadmodule "tm.so" 11 | modparam("tm", "fr_timer", 10000) 12 | modparam("tm", "fr_inv_timer", 180000) 13 | modparam("tm", "disable_6xx_block", 1) 14 | loadmodule "tmx.so" 15 | modparam("tmx", "precheck_trans", 1) 16 | 17 | #-------- Loading Mod SL ---------------------------------------------# 18 | 19 | loadmodule "sl.so" 20 | 21 | #-------- Loading Mod RR ---------------------------------------------# 22 | 23 | loadmodule "rr.so" 24 | modparam("rr", "append_fromtag", 0) 25 | modparam("rr", "enable_full_lr", 1) 26 | 27 | #-------- Loading Mod PV ---------------------------------------------# 28 | loadmodule "pv.so" 29 | modparam("pv", "varset", "defaultChannels=i:2") 30 | modparam("pv", "varset", "max_attempts=i:2") 31 | 32 | #-------- Loading Mod AVPOPS -----------------------------------------# 33 | loadmodule "avpops.so" 34 | 35 | #-------- Loading Mod JANNSON AND MONGODB ----------------------------# 36 | loadmodule "jansson.so" 37 | loadmodule "db_mongodb.so" 38 | loadmodule "ndb_mongodb.so" 39 | modparam("ndb_mongodb", "server", MONGOPATH) 40 | 41 | #-------- Loading Mod MAXFWD -----------------------------------------# 42 | loadmodule "maxfwd.so" 43 | 44 | #-------- Loading Mod USRLOC -----------------------------------------# 45 | loadmodule "usrloc.so" 46 | modparam("usrloc", "db_mode", 2) 47 | modparam("usrloc", "db_url", DBURL) 48 | modparam("usrloc", "use_domain", 1) 49 | modparam("usrloc", "matching_mode", 0) 50 | modparam("usrloc", "desc_time_order", 1) 51 | modparam("usrloc", "nat_bflag", FLB_NATB) 52 | modparam("usrloc", "db_insert_null", 1) 53 | 54 | #-------- Loading Mod REGISTRAR -------------------------------------# 55 | loadmodule "registrar.so" 56 | modparam("registrar", "received_avp", "") 57 | modparam("registrar", "received_param", "rcv") 58 | modparam("registrar", "outbound_mode", 0) 59 | 60 | #-------- Loading Mod AUTH -----------------------------------------# 61 | loadmodule "auth.so" 62 | modparam("auth", "use_domain", 1) 63 | modparam("auth", "nonce_count", 1) 64 | modparam("auth", "qop", "auth") 65 | 66 | #-------- Loading Mod DIALOG ---------------------------------------# 67 | loadmodule "dialog.so" 68 | modparam("dialog", "db_url", DBURL) 69 | modparam("dialog", "enable_stats", 1) 70 | modparam("dialog", "dlg_match_mode", 1) 71 | modparam("dialog", "db_mode", 1) 72 | modparam("dialog", "db_update_period", 45) 73 | modparam("dialog", "profiles_with_value", "endptChannels;custChannels;didChannels;") 74 | modparam("dialog", "dlg_flag", 4) 75 | 76 | #-------- Loading Mod NATHELPER ------------------------------------# 77 | 78 | loadmodule "nathelper.so" 79 | loadmodule "nat_traversal.so" 80 | modparam("nat_traversal", "keepalive_interval", 25) 81 | modparam("nat_traversal", "keepalive_from", "sip:keepalive@novanet.net") 82 | modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") 83 | 84 | #-------- Loading Mod IPOPS -----------------------------------------# 85 | loadmodule "ipops.so" 86 | 87 | #-------- Loading Mod UAC -------------------------------------------# 88 | loadmodule "uac.so" 89 | modparam("uac","restore_mode","none") 90 | modparam("uac","credential","username:domain:password") 91 | modparam("uac","auth_realm_avp","$avp(realm)") 92 | modparam("uac","auth_username_avp","$avp(siptrunk_username)") 93 | modparam("uac","auth_password_avp","$avp(siptrunk_password)") 94 | 95 | #-------- Loading Mod REDIS -----------------------------------------# 96 | loadmodule "ndb_redis.so" 97 | modparam("ndb_redis", "server", REDIS) 98 | 99 | #-------- Loading Mod RTPPROXY -------------------------------------# 100 | loadmodule "rtpengine.so" 101 | modparam("rtpengine", "rtpengine_sock", RTPENGINE) 102 | 103 | #-------- Loading Mod XLOG XHTTP SIPUTILS AND SANITY ---------------# 104 | loadmodule "textops.so" 105 | loadmodule "sdpops.so" 106 | loadmodule "debugger.so" 107 | loadmodule "siputils.so" 108 | loadmodule "xlog.so" 109 | loadmodule "sanity.so" 110 | loadmodule "xhttp.so" 111 | loadmodule "jsonrpcs.so" 112 | modparam("jsonrpcs", "transport", 1) 113 | 114 | #----------------------- Loading Module HASH TABLE ----------------------------# 115 | loadmodule "htable.so" 116 | modparam("htable", "htable", "rtpProxy=>size=8;autoexpire=300;") 117 | 118 | # ----- Loading Mod ACC ----------------------------------------------# 119 | 120 | loadmodule "acc.so" 121 | modparam("acc", "early_media", 0) 122 | modparam("acc", "report_ack", 0) 123 | modparam("acc", "report_cancels", 0) 124 | modparam("acc", "detect_direction", 0) 125 | modparam("acc", "db_insert_mode", 2) 126 | modparam("acc", "cdr_enable", 0) 127 | modparam("acc", "db_url", DBURL) 128 | modparam("acc", "cdrs_table", "sessions") 129 | modparam("acc", "db_flag", FLT_ACC) 130 | modparam("acc", "db_missed_flag", FLT_ACCMISSED) 131 | modparam("acc", "failed_transaction_flag", FLT_ACCFAILED) 132 | modparam("acc", "db_table_missed_calls", "failed_sessions") 133 | modparam("acc", "time_mode", 1) 134 | modparam("acc", "cdr_start_on_confirmed", 1) 135 | modparam("acc", "cdr_extra","rtpProxy=$dlg_var(rtpProxy);attempt=$dlg_var(call_attempts);src_ip=$dlg_var(src_ip);calling_num=$dlg_var(caller_id);called_number=$dlg_var(called_number);sipCode=$dlg_var(sip_code);sipReason=$dlg_var(sip_reason)") 136 | modparam("acc", "db_extra","rtpProxy=$dlg_var(rtpProxy);attempt=$dlg_var(call_attempts);src_ip=$dlg_var(src_ip);calling_num=$dlg_var(caller_id);called_number=$dlg_var(called_number);sipCode=$dlg_var(sip_code);sipReason=$dlg_var(sip_reason)") 137 | 138 | # ----- Loading EXEC ----------------------------------------------# 139 | loadmodule "exec.so" 140 | modparam("exec", "time_to_kill", 20) 141 | 142 | 143 | #---------------------- Routing Logic --------------------------------------# 144 | 145 | route { 146 | 147 | # per request initial checks 148 | route(REQINIT); 149 | # NAT detection 150 | route(NATDETECT); 151 | 152 | # CANCEL processing 153 | if (is_method("CANCEL")) { 154 | if (t_check_trans()){ 155 | route(RELAY); 156 | } 157 | exit; 158 | } 159 | 160 | #handle retransmissions 161 | if (!is_method("ACK")) { 162 | if(t_precheck_trans()) { 163 | t_check_trans(); 164 | exit; 165 | } 166 | t_check_trans(); 167 | } 168 | 169 | # handle requests within SIP dialogs 170 | route(WITHINDLG); 171 | 172 | ### only initial requests (no To tag) 173 | 174 | # handle retransmissions 175 | if(t_precheck_trans()) { 176 | t_check_trans(); 177 | exit; 178 | } 179 | 180 | t_check_trans(); 181 | 182 | # handle registrations 183 | if (is_method("REGISTER")){ 184 | sl_send_reply("404","Not Handled Here"); 185 | } 186 | 187 | remove_hf("Route"); 188 | if (is_method("INVITE")) { 189 | record_route(); 190 | } 191 | 192 | # account only INVITEs 193 | if (is_method("INVITE")) { 194 | route(call_source); 195 | } 196 | 197 | if ($rU==$null) { 198 | # request with no Username in RURI 199 | sl_send_reply("484","Address Incomplete"); 200 | exit; 201 | } 202 | 203 | } 204 | 205 | route[REQINIT] { 206 | 207 | if($ua =~ "(friendly-scanner|sipvicious|sipcli|VaxSIPUserAgent|voxalot|MizuPhone|Ozeki|tramb2017|voip|hello)") { 208 | # silent drop for scanners 209 | xlog("L_INFO","Blocking scanners and pushing IPs to iptables"); 210 | exec_avp("iptables -A INPUT -s $si -j DROP"); 211 | sl_send_reply("403", "Forbidden"); 212 | exit; 213 | } 214 | 215 | 216 | if (!mf_process_maxfwd_header("10")) { 217 | sl_send_reply("483","Too Many Hops"); 218 | exit; 219 | } 220 | 221 | if(is_method("OPTIONS") && uri==myself && $rU==$null) { 222 | sl_send_reply("200","Keepalive"); 223 | exit; 224 | } 225 | 226 | if(!sanity_check("1511", "7")) { 227 | xlog("Malformed SIP message from $si:$sp\n"); 228 | exit; 229 | } 230 | } 231 | route[WITHINDLG] { 232 | if (!has_totag()) return; 233 | 234 | # sequential request withing a dialog should 235 | # take the path determined by record-routing 236 | if (loose_route()) { 237 | route(DLGURI); 238 | if (is_method("BYE")) { 239 | route(rtp_engine); 240 | xlog("L_INFO","BYE in LR ; handling RTP Engine"); 241 | 242 | } else if ( is_method("ACK") ) { 243 | 244 | route(NATMANAGE); 245 | 246 | } else if ( is_method("NOTIFY") ) { 247 | # Add Record-Route for in-dialog NOTIFY as per RFC 6665. 248 | record_route(); 249 | } 250 | route(RELAY); 251 | exit; 252 | } 253 | 254 | 255 | if ( is_method("ACK") ) { 256 | if ( t_check_trans() ) { 257 | # no loose-route, but stateful ACK; 258 | # must be an ACK after a 487 259 | # or e.g. 404 from upstream server 260 | route(RELAY); 261 | exit; 262 | } else { 263 | # ACK without matching transaction ... ignore and discard 264 | exit; 265 | } 266 | } 267 | sl_send_reply("404","Not here"); 268 | exit; 269 | } 270 | 271 | route[DLGURI] { 272 | 273 | if(!isdsturiset()) { 274 | handle_ruri_alias(); 275 | } 276 | 277 | return; 278 | } 279 | 280 | branch_route[MANAGE_BRANCH] { 281 | xdbg("new branch [$T_branch_idx] to $ru\n"); 282 | route(NATMANAGE); 283 | } 284 | 285 | onreply_route[MANAGE_REPLY] { 286 | xdbg("incoming reply\n"); 287 | if(status=~"[12][0-9][0-9]") { 288 | route(NATMANAGE); 289 | } 290 | } 291 | 292 | failure_route[MANAGE_FAILURE] { 293 | route(NATMANAGE); 294 | 295 | if (t_is_canceled()) exit; 296 | } 297 | 298 | 299 | 300 | route[NATDETECT] { 301 | 302 | force_rport(); 303 | if (nat_uac_test("19")) { 304 | if (is_method("REGISTER")) { 305 | fix_nated_register(); 306 | } else { 307 | if(is_first_hop()) { 308 | set_contact_alias(); 309 | } 310 | } 311 | setflag(FLT_NATS); 312 | } 313 | 314 | return; 315 | } 316 | 317 | route[NATMANAGE] { 318 | 319 | if (is_request()) { 320 | if(has_totag()) { 321 | if(check_route_param("nat=yes")) { 322 | setbflag(FLB_NATB); 323 | } 324 | } 325 | } 326 | if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; 327 | 328 | if (is_request()) { 329 | if (!has_totag()) { 330 | if(t_is_branch_route()) { 331 | add_rr_param(";nat=yes"); 332 | } 333 | } 334 | } 335 | if (is_reply()) { 336 | if(isbflagset(FLB_NATB)) { 337 | if(is_first_hop()) 338 | set_contact_alias(); 339 | } 340 | } 341 | return; 342 | } 343 | 344 | 345 | event_route[core:worker-one-init]{ 346 | exec_avp("cat /usr/local/kamailio/etc/kamailio/scripts/scripts.txt | redis-cli --pipe"); 347 | xlog("L_INFO","***** Kamailio : InboundSIP Project has started ****"); 348 | } 349 | 350 | route[RELAY] { 351 | 352 | if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { 353 | if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); 354 | } 355 | 356 | if (is_method("INVITE|SUBSCRIBE|UPDATE")) { 357 | if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY"); 358 | } 359 | 360 | if (is_method("INVITE")) { 361 | 362 | if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); 363 | } 364 | 365 | if (!t_relay()) { 366 | sl_reply_error(); 367 | } 368 | exit; 369 | } 370 | 371 | event_route[xhttp:request] { 372 | xlog("L_INFO","http request "); 373 | 374 | if ($hu =~ "^/RPC") { 375 | xlog("L_INFO","http request /RPC"); 376 | jsonrpc_dispatch(); 377 | } else { 378 | xlog("L_INFO","http request "); 379 | xhttp_reply("200", "OK", "text/html","Wrong URL $hu"); 380 | } 381 | return; 382 | } 383 | -------------------------------------------------------------------------------- /call_source.cfg: -------------------------------------------------------------------------------- 1 | route[call_source]{ 2 | 3 | $avp(called_number)=$rU; 4 | $avp(caller_id)=$fU; 5 | $avp(src_ip)=$si; 6 | 7 | //check if inbound call 8 | redis_cmd("redis","HMGET did:$avp(called_number) custId carrierId countryId billType channel rtpEngine routingType","r"); 9 | if($redis(r=>value[2])!=0){ 10 | $avp(custId)=$redis(r=>value[0]); 11 | $avp(carrierId)=$redis(r=>value[1]); 12 | $avp(countryId)=$redis(r=>value[2]); 13 | $avp(billType)=$redis(r=>value[3]); 14 | $avp(didChannels)=$redis(r=>value[4]); 15 | $avp(rtpEngine)=$redis(r=>value[5]); 16 | if($(avp(rtpEngine){s.int})==1){ 17 | xlog("L_INFO","Setting rtpEngine for inbound call"); 18 | $sht(rtpProxy=>mip_$ci)=1; 19 | } 20 | $avp(routingType)=$redis(r=>value[6]); 21 | 22 | $avp(direction)="In"; 23 | //validate src for carrier ip 24 | route(validate_carrier_ip); 25 | if(isflagset(SIP_IP_OK)){ 26 | xlog("L_INFO","Source is Authorised Carrier $si"); 27 | if($avp(routingType))!=0{ 28 | route(is_carrier); 29 | }else{ 30 | $avp(sip_code)="404"; 31 | $avp(sip_reason)="Not Routed"; 32 | route(SET_CDR); 33 | acc_db_request("Not Routed", "failed_calls"); 34 | sl_send_reply("404","Not Routed"); 35 | exit; 36 | } 37 | exit; 38 | } 39 | } 40 | 41 | xlog("L_INFO","Forbidden for $si $ua"); 42 | sl_send_reply("403", "Forbidden"); 43 | exit; 44 | 45 | } 46 | 47 | route[validate_carrier_ip]{ 48 | 49 | 50 | redis_cmd("redis","SMEMBERS carrierIPs:$avp(carrierId)","r"); 51 | $var(count)=$redis(r=>size); 52 | $var(iterate)=0; 53 | while ($(var(count){s.int})>=$null && $var(count)!=""){ 54 | $avp((ip_access)[$var(iterate)])=$redis(r=>value[$var(iterate)]); 55 | xlog("L_INFO","ip_access:[$var(iterate)] : $avp((ip_access)[$var(iterate)])"); 56 | if (is_in_subnet("$si", "$avp((ip_access)[$var(iterate)])")) { 57 | xlog("L_INFO", "$var(count) is in the subnet $var(count) $var(iterate)\n"); 58 | setflag(SIP_IP_OK); 59 | } 60 | $var(count)=$var(count)-1; 61 | $var(iterate)=$var(iterate)+1; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /carrier.cfg: -------------------------------------------------------------------------------- 1 | route[is_carrier]{ 2 | xlog("L_INFO","Inroute source is carrier"); 3 | //HMSET customer:7045 routeId 23456 balance 700 4 | redis_cmd("redis","HGET customer:$avp(custId) routeId","r"); 5 | $avp(routeId)=$redis(r=>value); 6 | 7 | //check abuse caller 8 | redis_cmd("redis","SISMEMBER abuse:$avp(called_number) $avp(caller_id)","r"); 9 | if($redis(r=>value[0])==1){ 10 | sl_send_reply("486","Busy Here"); 11 | exit; 12 | } 13 | 14 | route(did_channels_check); 15 | 16 | if($avp(routingType)==1){ 17 | //direct routing could be IP address or Phone number or Username 18 | redis_cmd("redis","HMGET direct:$avp(called_number) dstUri dnis","r"); 19 | if($redis(r=>value[0])!=0){ 20 | $avp(dstUri)=$redis(r=>value[0]); 21 | $avp(dnis)=$redis(r=>value[1]); 22 | } 23 | }else{ 24 | sl_send_reply("500","Internal Server Error"); 25 | exit; 26 | 27 | } 28 | route(rtp_engine); 29 | route(relay_out); 30 | 31 | } 32 | 33 | route[relay_out]{ 34 | xlog("L_INFO","Call routing identity is $avp(dstUri) rtpProxy = $sht(rtpProxy=>mip_$ci)"); 35 | $avp(identity)=$(avp(dstUri){s.select,0,:}); 36 | $avp(port)=$(avp(dstUri){s.select,1,:}); 37 | $avp(dstUrilen)=$(avp(identity){s.len}); 38 | 39 | if(is_ip($avp(identity))){ 40 | //TODO dnis for the called number 41 | //call is routed to IP address and the DNIS should be same as called number 42 | xlog("L_INFO","identity is IP Address identity : $avp(identity) and port : $avp(port)"); 43 | $rU=$avp(dnis); 44 | $rd=$avp(identity); 45 | $rp=$avp(port); 46 | $var(duri) = "sip:"+$rU+"@"+$avp(identity)+":"+$avp(port); 47 | $du = $var(duri); 48 | t_on_failure("dst_failure"); 49 | t_on_reply("dst_reply"); 50 | if(!t_relay()) { 51 | sl_reply_error(); 52 | exit; 53 | } 54 | exit; 55 | } 56 | 57 | if($avp(dstUrilen)>7){ 58 | xlog("L_INFO","PSTN number ROUTING"); 59 | $rU=$avp(dnis); 60 | $avp(called_number)=$avp(dnis); 61 | route(pstn_forward); 62 | } 63 | 64 | } 65 | 66 | onreply_route[dst_reply]{ 67 | 68 | if(status == "180" || status == "183") { 69 | //xlog("L_INFO","ringing"); 70 | $avp(inv_timer) = 45; 71 | //do not fail-over call now as we have a provisional response 72 | setflag(RINGING); 73 | } 74 | 75 | if(status == "200") { 76 | if(!isflagset(ANSWERED)){ 77 | xlog("L_INFO","the call was answered and ANSWERED flag was will be set now"); 78 | setflag(ANSWERED); 79 | $avp(sip_code)=$T_reply_code; 80 | $avp(sip_reason)=$T_reply_reason; 81 | route(SET_CDR); 82 | route(rtp_engine); 83 | } 84 | } 85 | 86 | } 87 | 88 | failure_route[dst_failure]{ 89 | xlog("L_INFO","failure route ; $T_reply_code"); 90 | if(t_is_canceled() || t_check_status("487") || t_check_status("404") || t_check_status("486")) { 91 | xlog("L_INFO","Incase of a cancelled / Invalid call Attempt $rm ; Inbound call"); 92 | setflag(CANCELLED); 93 | $avp(sip_code)=$T_reply_code; 94 | $avp(sip_reason)=$T_reply_reason; 95 | xlog("L_INFO","cancelled call ; sip_code :$avp(sip_code) ; sip_reason: $avp(sip_reason)"); 96 | route(SET_CDR); 97 | route(rtp_engine); 98 | acc_db_request("Canceled call", "failed_sessions"); 99 | exit; 100 | } 101 | 102 | if(t_check_status("408|403")){ 103 | xlog("L_INFO","failure route 408"); 104 | $avp(inv_timer) = 17; 105 | $avp(sip_code)=$T_reply_code; 106 | $avp(sip_reason)=$T_reply_reason; 107 | route(SET_CDR); 108 | acc_db_request("Canceled call", "failed_sessions"); 109 | exit; 110 | } 111 | 112 | if(t_check_status("480")){ 113 | xlog("L_INFO","failure route 480"); 114 | setflag(CANCELLED); 115 | $avp(sip_code)=$T_reply_code; 116 | $avp(sip_reason)=$T_reply_reason; 117 | route(SET_CDR); 118 | route(rtp_engine); 119 | acc_db_request("Temp Unavailable", "failed_sessions"); 120 | exit; 121 | } 122 | 123 | if(isflagset(RINGING)){ 124 | //Incase a call fails after a 18X provisional response 125 | xlog("L_INFO","The call had already received a 180 0r 183 and then failed $T_reply_code $rm"); 126 | $avp(sip_code)=$T_reply_code; 127 | $avp(sip_reason)=$T_reply_reason; 128 | route(SET_CDR); 129 | acc_db_request("RINGING", "failed_sessions"); 130 | resetflag(RINGING); 131 | exit; 132 | } 133 | 134 | } 135 | 136 | -------------------------------------------------------------------------------- /cdr.cfg: -------------------------------------------------------------------------------- 1 | route[SET_CDR]{ 2 | xlog("L_INFO","entered the CDR route"); 3 | $var(call_attempts)=$avp(call_attempts)+1; 4 | $dlg_var(call_attempts)=$var(call_attempts); 5 | $dlg_var(called_number)=$avp(called_number); 6 | $dlg_var(caller_id)=$avp(caller_id); 7 | $dlg_var(src_ip)=$avp(src_ip); 8 | $dlg_var(customerId)=$avp(customerId); 9 | $dlg_var(carrierId)=$avp(carrierId); 10 | $dlg_var(countryId)=$avp(countryId); 11 | $dlg_var(sip_code)=$avp(sip_code); 12 | $dlg_var(sip_reason)=$avp(sip_reason); 13 | $dlg_var(rtpProxy)=$sht(rtpProxy=>mip_$ci); 14 | 15 | } -------------------------------------------------------------------------------- /channel_limit.cfg: -------------------------------------------------------------------------------- 1 | route[did_channels_check]{ 2 | get_profile_size("didChannels", "$avp(called_number)", "$var(did_channels_cnt)"); 3 | $avp(totalChannels)=$var(defaultChannels)+$(avp(did_channels){s.int}); 4 | xlog("L_INFO","the DID channels current count is $var(did_channels_cnt) ; while the did_channels are $avp(did_channels); totalChannels: $avp(totalChannels)"); 5 | if($(var(did_channels_cnt){s.int}) >= $(avp(totalChannels){s.int})) { 6 | $avp(sip_code)="486"; 7 | $avp(src_failure_reason)="DID channels Exceeded"; 8 | sl_send_reply("486", "DID channels Exceeded"); 9 | exit; 10 | } else{ 11 | set_dlg_profile("didChannels", "$avp(called_number)"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /kamailio.cfg: -------------------------------------------------------------------------------- 1 | include_file "basic.cfg" 2 | include_file "channel_limit.cfg" 3 | include_file "pstn_dest.cfg" 4 | include_file "call_source.cfg" 5 | include_file "rtpengine.cfg" 6 | include_file "cdr.cfg" 7 | include_file "carrier.cfg" 8 | -------------------------------------------------------------------------------- /pstn_dest.cfg: -------------------------------------------------------------------------------- 1 | route[pstn_forward]{ 2 | 3 | $avp(position)=1; 4 | $avp(dstlist_id)=0; 5 | route(get_carriers); 6 | 7 | if($avp(failovers)==$null){ 8 | $avp(sip_code)="500"; 9 | $avp(sip_reason)="No More Carriers"; 10 | route(SET_CDR); 11 | acc_db_request("No More Carriers", "failed_calls"); 12 | xlog("L_INFO","No More Carriers"); 13 | sl_send_reply("500","No More Carriers"); 14 | exit; 15 | } 16 | 17 | $avp(max_attempts)=$var(max_attempts); 18 | xlog("L_INFO","The max call attempts are $avp(max_attempts)"); 19 | $avp(call_attempts)=0; 20 | route(carrier_routing); 21 | exit; 22 | } 23 | 24 | route[get_carriers]{ 25 | redis_cmd("redis","evalsha ed298d3856ca8aeadde716def3ac0d9a72059740 0 $avp(routeId) $avp(dstlist_id) $avp(position) $avp(called_number)","r"); 26 | if($redis(r=>value[0])!=0){ 27 | $avp(failovers)=$redis(r=>size); 28 | $avp(carrier)=$redis(r=>value[0]); 29 | $var(c)=$avp(failovers)-1; 30 | while($var(c)!=-1){ 31 | $(avp(carrier)[$var(c)]) = $redis(r=>value[$var(c)]); 32 | #--xlog("L_INFO","Carrier $var(c) $redis(r=>value[$var(c)])"); 33 | $var(c)=$var(c)-1; 34 | } 35 | 36 | } 37 | } 38 | 39 | route[carrier_routing]{ 40 | xlog("L_INFO","call variables $avp(call_attempts) : $avp(max_attempts) "); 41 | if($avp(call_attempts)<$avp(max_attempts)){ 42 | 43 | if($avp(call_attempts)<$avp(failovers)){ 44 | 45 | $avp(carrier_id)=$(avp(carrier)[$avp(call_attempts)]{s.select,0,:}); 46 | $avp(dst_carrier_rate)=$(avp(carrier)[$avp(call_attempts)]{s.select,1,:}); 47 | $avp(carrier_prefix)=$(avp(carrier)[$avp(call_attempts)]{s.select,2,:}); 48 | xlog("L_INFO", "carrier_routing: carrier Id is $avp(carrier_id) ; dst_carrier_rate : $avp(dst_carrier_rate) ; carrier_prefix: $avp(carrier_prefix) Attempt no : $avp(call_attempts)"); 49 | 50 | route(carrier_fetch_ip); 51 | route(carrier_relay); 52 | } 53 | }else{ 54 | xlog("L_INFO","MAX Attempts reached"); 55 | } 56 | exit; 57 | } 58 | 59 | 60 | route[carrier_fetch_ip]{ 61 | 62 | redis_cmd("redis","SRANDMEMBER carrierOutIps:$avp(carrier_id)","r"); 63 | $avp(domain_port)=$redis(r=>value); 64 | if(is_avp_set("$avp(domain_port)")){ 65 | $avp(domain)=$(avp(domain_port){s.select,0,:}); 66 | $avp(port)=$(avp(domain_port){s.select,1,:}); 67 | }else{ 68 | $avp(call_attempts)=$avp(call_attempts)+1; 69 | //xlog("L_INFO","INC call_attempts $avp(call_attempts)"); 70 | route(carrier_routing); 71 | } 72 | } 73 | 74 | route[carrier_relay]{ 75 | route(rtp_engine); 76 | $rd=$avp(domain); 77 | $rp=$avp(port); 78 | t_on_failure("carrier_failover"); 79 | t_on_reply("carrier_reply"); 80 | 81 | $var(duri) = "sip:"+$rU+"@"+$rd+":"+$rp; 82 | $du = $var(duri); 83 | 84 | xlog("L_INFO","relayed $avp(domain):$avp(port) $avp(called_number) $var(duri) $du"); 85 | if (!t_relay()) { 86 | sl_reply_error(); 87 | } 88 | exit; 89 | } 90 | 91 | failure_route[carrier_failover]{ 92 | 93 | if(t_is_canceled() || t_check_status("487") || t_check_status("404") ) { 94 | //Incase of a cancelled / Invalid call Attempt 95 | xlog("L_INFO","Incase of a cancelled / Invalid call Attempt $rm"); 96 | setflag(CANCELLED); 97 | $avp(sip_code)=$T_reply_code; 98 | $avp(sip_reason)=$T_reply_reason; 99 | route(SET_CDR); 100 | route(rtp_engine); 101 | acc_db_request("Some comment", "failed_calls"); 102 | exit; 103 | } 104 | 105 | 106 | if(isflagset(RINGING)){ 107 | //Incase a call fails after a 18X provisional response || t_check_status("486") 108 | xlog("L_INFO","The call had already received a 180 0r 183 and then failed"); 109 | $avp(sip_code)=$T_reply_code; 110 | $avp(sip_reason)=$T_reply_reason; 111 | route(SET_CDR); 112 | acc_db_request("FAILED AFTER SESSION PROGRESS", "failed_calls"); 113 | setflag(CANCELLED); 114 | route(rtp_engine); 115 | resetflag(RINGING); 116 | exit; 117 | } 118 | 119 | //Incase a call fails otherwise 120 | $avp(sip_code)=$T_reply_code; 121 | $avp(sip_reason)=$T_reply_reason; 122 | route(SET_CDR); 123 | acc_db_request("Some comment", "failed_sessions"); 124 | xlog("L_INFO","InRoute carrier_failover route $avp(call_attempts) ; $T_reply_code"); 125 | $avp(call_attempts)=$avp(call_attempts)+1; 126 | route(carrier_routing); 127 | exit; 128 | } 129 | 130 | onreply_route[carrier_reply]{ 131 | 132 | if(status == "100") { 133 | $avp(inv_timer) = 17; 134 | //xlog("L_INFO","100 trying"); 135 | } 136 | 137 | if(status == "180" || status == "183") { 138 | //xlog("L_INFO","ringing"); 139 | $avp(inv_timer) = 45; 140 | //do not fail-over call now as we have a provisional response 141 | setflag(RINGING); 142 | } 143 | 144 | if(status == "200") { 145 | if(!isflagset(ANSWERED)){ 146 | xlog("L_INFO","received 200ok in dst_carrier route $ci answer_timestamp : $TS"); 147 | setflag(ANSWERED); 148 | $avp(sip_code)=$T_reply_code; 149 | $avp(sip_reason)=$T_reply_reason; 150 | route(SET_CDR); 151 | route(rtp_engine); 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /redisFiller.txt: -------------------------------------------------------------------------------- 1 | HMSET phoneNumber:919503338275 customerId 7045 carrierId 1234 countryId 1 billType 1 channels 2 rtpProxied 1 routStrat 1 2 | SADD carrierIPAddrs:1234 198.24.63.4 3 | HMSET directRoute:919503338275 dstUri 919503338275 dnis 919503338275 4 | HMSET directRoute:919503338275 dstUri 203.153.53.180:5060 dnis 919503338275 5 | SADD carrierEndpoints:1234 203.153.53.180:5060 6 | HMSET customer:7045 routeId 23456 balance 700 7 | ZADD routes:23456:0 1 carrierId 8 | HMSET carrierRates:1234:37 91 0.3456 919 0.3456 9 | HMSET dialCode:9195 destName IND-MOBILE dstListId 37 10 | script load "local function split(s, delimiter) local result = {}; for match in (s..delimiter):gmatch('(.-)'..delimiter) do table.insert(result, match); end return result; end local function custom(number,carrierId,dstLstId) for i = 7, 1, -1 do local prefix = string.sub(number, 1, i); local carrierRate = redis.call('HMGET', 'carrierRates:' .. carrierId .. ':' .. dstLstId, prefix); local carrierRatesize = table.maxn(carrierRate) if carrierRatesize~=nil and carrierRate[1]~=false and carrierRate[1]~=nil then return carrierRate[1]..':'..prefix end end end local routeId = ARGV[1] local dstLstId = ARGV[2] local position = ARGV[3] local number = ARGV[4] local carrierRates = {} local mytable ={} local newtable = {} local carrierId = redis.call('ZRANGEBYSCORE', 'routes:' .. routeId .. ':' .. dstLstId, position, position) local size = table.maxn(carrierId) local d=1 while size>0 do carrierRates[d]=custom(number,carrierId[size],dstLstId) if type(carrierRates[d])~='nil' then mytable[1]= carrierId[size] local splitdata=split(carrierRates[d],':') mytable[2]=splitdata[1] mytable[3]=splitdata[2] newtable[d]=mytable mytable={} d=d+1 end size=size-1 end table.sort(newtable, function(a, b) return a[2] < b[2] end) local sorted= {} for k,v in ipairs(newtable) do sorted[k]=v[1]..':'..v[2]..':'..v[3] end return sorted" 11 | 12 | 13 | HMSET phoneNumber:23418889700 customerId 2222 carrierId 1111 countryId 1 billType 1 channels 2 rtpProxied 1 routStrat 1 14 | HMSET phoneNumber:23418889701 customerId 2222 carrierId 1111 countryId 1 billType 1 channels 2 rtpProxied 1 routStrat 1 15 | SADD carrierIPAddrs:1111 188.244.114.8 203.153.53.130 16 | HMSET directRoute:23418889700 dstUri 14703993047 dnis 14703993047 17 | HMSET directRoute:23418889701 dstUri 14703993047 dnis 14703993047 18 | HMSET customer:2222 routeId 3333 balance 700 19 | ZADD routes:3333:0 1 4444 20 | SADD carrierEndpoints:4444 178.62.27.239:5060 21 | 22 | HMSET carrierRates:4444:0 14703993047 0.77 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /rtpengine.cfg: -------------------------------------------------------------------------------- 1 | route[rtp_engine]{ 2 | //offer 3 | if (is_method("INVITE") && $sht(rtpProxy=>mip_$ci)==1 && !isflagset(RTPE)) { 4 | setflag(RTPE); 5 | xlog("L_INFO","rtpengine offer command"); 6 | rtpengine_manage("ICE=remove replace-session-connection"); 7 | } 8 | 9 | //answer 10 | if(isflagset(RTPE) && $T_reply_code==200){ 11 | //resetflag(RTPE); 12 | xlog("L_INFO","rtpengine answer command"); 13 | rtpengine_manage("ICE=remove replace-session-connection"); 14 | } 15 | 16 | //delete 17 | if (is_method("BYE") && $sht(rtpProxy=>mip_$ci)==1) { 18 | $sht(rtpProxy=>mip_$ci)=$null; 19 | xlog("L_INFO","rtpengine delete command"); 20 | rtpengine_manage("ICE=remove replace-session-connection"); 21 | 22 | } 23 | 24 | //cancelled 25 | if(isflagset(CANCELLED)){ 26 | $sht(rtpProxy=>mip_$ci)=$null; 27 | xlog("L_INFO","rtpengine delete command; cancelled call"); 28 | rtpengine_manage("ICE=remove replace-session-connection"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scripts.txt: -------------------------------------------------------------------------------- 1 | script load "local function split(s, delimiter) local result = {}; for match in (s..delimiter):gmatch('(.-)'..delimiter) do table.insert(result, match); end return result; end local function custom(number,carrierId,dstLstId) for i = 7, 1, -1 do local prefix = string.sub(number, 1, i); local carrierRate = redis.call('HMGET', 'carrierRates:' .. carrierId .. ':' .. dstLstId, prefix); local carrierRatesize = table.maxn(carrierRate) if carrierRatesize~=nil and carrierRate[1]~=false and carrierRate[1]~=nil then return carrierRate[1]..':'..prefix end end end local routeId = ARGV[1] local dstLstId = ARGV[2] local position = ARGV[3] local number = ARGV[4] local carrierRates = {} local mytable ={} local newtable = {} local carrierId = redis.call('ZRANGEBYSCORE', 'routes:' .. routeId .. ':' .. dstLstId, position, position) local size = table.maxn(carrierId) local d=1 while size>0 do carrierRates[d]=custom(number,carrierId[size],dstLstId) if type(carrierRates[d])~='nil' then mytable[1]= carrierId[size] local splitdata=split(carrierRates[d],':') mytable[2]=splitdata[1] mytable[3]=splitdata[2] newtable[d]=mytable mytable={} d=d+1 end size=size-1 end table.sort(newtable, function(a, b) return a[2] < b[2] end) local sorted= {} for k,v in ipairs(newtable) do sorted[k]=v[1]..':'..v[2]..':'..v[3] end return sorted" -------------------------------------------------------------------------------- /vars.cfg: -------------------------------------------------------------------------------- 1 | #!KAMAILIO 2 | #----------------------- Custom Defined Variables ----------------------------# 3 | #!substdef "!MY_IP_ADDR!192.168.2.46!g" 4 | #!substdef "!MY_OB_PORT!5060!g" 5 | #!define DBURL "mongodb://192.168.2.46:27017/kamailio" 6 | #!define REDIS "name=redis;addr=127.0.0.1;port=6379;db=0" 7 | #!define RTPENGINE "udp:192.168.2.46:25061" 8 | #!define MONGOPATH "name=mgs1;uri='mongodb://192.168.2.46:27017/kamailio'" 9 | 10 | 11 | #------------- FLAGS -------------------------------------# 12 | #!define FLT_ACC 1 13 | #!define FLT_ACCMISSED 2 14 | #!define FLT_ACCFAILED 3 15 | #!define FLT_NATS 5 16 | #!define FLB_NATB 6 17 | #!define FLB_NATSIPPING 7 18 | #!define SIP_IP_OK 8 19 | #!define RINGING 10 20 | #!define CUST_AUTH_OK 11 21 | #!define RTPE 13 22 | #!define CANCELLED 14 23 | #!define ANSWERED 17 24 | 25 | 26 | #------------- Global Parameters --------------------------# 27 | debug=2 28 | log_stderror=no 29 | memdbg=5 30 | memlog=5 31 | async_workers=8 32 | 33 | #------------ Logs to /var/log/kamailio.log ----------------# 34 | 35 | log_facility=LOG_LOCAL1 36 | fork=yes 37 | children=2 38 | listen=udp:MY_IP_ADDR:MY_OB_PORT 39 | listen=tcp:MY_IP_ADDR:10443 40 | tcp_connection_lifetime=3605 41 | tcp_accept_no_cl=yes 42 | disable_tcp=no 43 | --------------------------------------------------------------------------------