├── README.md ├── kamailio-ws.cfg ├── modules.cfg └── run-rtpproxy.sh /README.md: -------------------------------------------------------------------------------- 1 | kamailio-ws 2 | =========== 3 | 4 | Kamailio scripts to call from websocket UA to classic UA, and vice versa. 5 | 6 | I will write a blog entry explaining how this works, but in a nutshell: 7 | 8 | - this script is configured to run behind NAT, port TCP 10080 and TCP/UDP 5090 are exposed to the Internet 9 | - you have to create valid users using, preferably, "kamctl add ..." 10 | - RTP ports should be open in range 30k-35k, inclusive 11 | - I used jssip as webrtc SIP UA: http://tryit.jssip.net/ 12 | - Always disable video before placing a call from jssip UA (sometimes, it works) 13 | - Tested with Chrome, FF, and Opera 14 | - Remember to also change the record_route_preset() value 15 | - I tested calls between: 16 | - jssip to csipsimple 17 | - csipsimple to jssip 18 | - csipsimple to csipsimple 19 | -------------------------------------------------------------------------------- /kamailio-ws.cfg: -------------------------------------------------------------------------------- 1 | #!KAMAILIO 2 | 3 | #!define DBURL "mysql://kamailio:kamailio@localhost/kamailio" 4 | 5 | #!substdef "!MY_IP_ADDR!162.243.148.36!g" 6 | #!substdef "!MY_DOMAIN!sf1.caruizdiaz.com!g" 7 | #!substdef "!MY_WS_PORT!10080!g" 8 | #!substdef "!MY_WSS_PORT!10443!g" 9 | 10 | #!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g" 11 | #!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g" 12 | 13 | #!define WITH_NAT 14 | #!define FLB_NATB 6 15 | #!define FLB_NATSIPPING 7 16 | #!define FLT_NATS 5 17 | #!define FLT_OUT 8 18 | 19 | include_file "webrtc/modules.cfg" 20 | 21 | listen=MY_IP_ADDR:5090 22 | listen=MY_WS_ADDR 23 | advertised_address = "sf1.caruizdiaz.com" 24 | alias = "sf1.caruizdiaz.com" 25 | 26 | request_route { 27 | 28 | if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) 29 | && !(proto == WS || proto == WSS))) { 30 | xlog("L_WARN", "SIP request received on $Rp\n"); 31 | sl_send_reply("403", "Forbidden"); 32 | exit; 33 | } 34 | 35 | # per request initial checks 36 | route(REQINIT); 37 | 38 | # NAT detection 39 | route(NATDETECT); 40 | 41 | # CANCEL processing 42 | if (is_method("CANCEL")) { 43 | if (t_check_trans()) { 44 | route(RELAY); 45 | } 46 | exit; 47 | } 48 | 49 | # handle requests within SIP dialogs 50 | route(WITHINDLG); 51 | 52 | ### only initial requests (no To tag) 53 | 54 | t_check_trans(); 55 | 56 | if (!is_method("REGISTER|INVITE|ACK|BYE|CANCEL|PRACK|MESSAGE|INFO|UPDATE")) { 57 | sl_send_reply("405", "Method not allowed"); 58 | exit; 59 | } 60 | 61 | # authentication 62 | route(AUTH); 63 | 64 | # record routing for dialog forming requests (in case they are routed) 65 | # - remove preloaded route headers 66 | remove_hf("Route"); 67 | if (is_method("INVITE|SUBSCRIBE")) 68 | record_route_preset("sf1.caruizdiaz.com:5090;nat=yes"); 69 | 70 | # handle registrations 71 | route(REGISTRAR); 72 | 73 | if ($rU==$null) { 74 | # request with no Username in RURI 75 | sl_send_reply("484","Address Incomplete"); 76 | exit; 77 | } 78 | 79 | if (!is_method("INVITE")) { 80 | route(RELAY); 81 | exit; 82 | } 83 | 84 | route(LOCATION); 85 | } 86 | 87 | route[SETUP_BY_TRANSPORT] { 88 | 89 | if ($ru =~ "transport=ws") { 90 | xlog("L_INFO", "Request going to WS"); 91 | if(sdp_with_transport("RTP/SAVPF")) { 92 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=force"); 93 | t_on_reply("REPLY_WS_TO_WS"); 94 | return; 95 | } 96 | 97 | # rtpengine_manage("froc+SP"); 98 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=force RTP/SAVPF"); 99 | t_on_reply("REPLY_FROM_WS"); 100 | } 101 | else if ($proto =~ "ws") { 102 | xlog("L_INFO", "Request coming from WS"); 103 | # rtpengine_manage("froc-sp"); 104 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=remove RTP/AVP"); 105 | t_on_reply("REPLY_TO_WS"); 106 | } 107 | else { 108 | xlog("L_INFO", "This is a classic phone call"); 109 | # rtpengine_manage("co"); 110 | rtpengine_manage("replace-origin replace-session-connection"); 111 | t_on_reply("MANAGE_CLASSIC_REPLY"); 112 | } 113 | } 114 | 115 | event_route[xhttp:request] { 116 | set_reply_close(); 117 | set_reply_no_connect(); 118 | 119 | if ($Rp != MY_WS_PORT 120 | #!ifdef WITH_TLS 121 | && $Rp != MY_WSS_PORT 122 | #!endif 123 | ) { 124 | xlog("L_WARN", "HTTP request received on $Rp\n"); 125 | xhttp_reply("403", "Forbidden", "", ""); 126 | exit; 127 | } 128 | 129 | xlog("L_INFO", "HTTP Request Received\n"); 130 | 131 | if ($hdr(Upgrade)=~"websocket" 132 | && $hdr(Connection)=~"Upgrade" 133 | && $rm=~"GET") { 134 | 135 | # Validate Host - make sure the client is using the correct 136 | # alias for WebSockets 137 | if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) { 138 | xlog("L_WARN", "Bad host $hdr(Host)\n"); 139 | xhttp_reply("403", "Forbidden", "", ""); 140 | exit; 141 | } 142 | 143 | if (ws_handle_handshake()) 144 | { 145 | # Optional... cache some information about the 146 | # successful connection 147 | exit; 148 | } 149 | } 150 | 151 | xhttp_reply("404", "Not Found", "", ""); 152 | } 153 | 154 | route[RELAY] { 155 | if (is_method("INVITE")) { 156 | route(SETUP_BY_TRANSPORT); 157 | if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); 158 | } 159 | 160 | if (!t_relay()) { 161 | sl_reply_error(); 162 | } 163 | exit; 164 | } 165 | 166 | # Per SIP request initial checks 167 | route[REQINIT] { 168 | #!ifdef WITH_ANTIFLOOD 169 | # flood dection from same IP and traffic ban for a while 170 | # be sure you exclude checking trusted peers, such as pstn gateways 171 | # - local host excluded (e.g., loop to self) 172 | if(src_ip!=myself) { 173 | if($sht(ipban=>$si)!=$null) { 174 | # ip is already blocked 175 | xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n"); 176 | exit; 177 | } 178 | if (!pike_check_req()) { 179 | xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n"); 180 | $sht(ipban=>$si) = 1; 181 | exit; 182 | } 183 | } 184 | #!endif 185 | 186 | if (!mf_process_maxfwd_header("10")) { 187 | sl_send_reply("483","Too Many Hops"); 188 | exit; 189 | } 190 | 191 | if(!sanity_check("1511", "7")) { 192 | xlog("Malformed SIP message from $si:$sp\n"); 193 | exit; 194 | } 195 | } 196 | 197 | # Handle requests within SIP dialogs 198 | route[WITHINDLG] { 199 | if (has_totag()) { 200 | # sequential request withing a dialog should 201 | # take the path determined by record-routing 202 | if (loose_route()) { 203 | 204 | if ($du == "") { 205 | if (!handle_ruri_alias()) { 206 | xlog("L_ERR", "Bad alias <$ru>\n"); 207 | sl_send_reply("400", "Bad Request"); 208 | exit; 209 | } 210 | } 211 | # else 212 | # route(DLGURI); 213 | 214 | xlog("==> duri=[$du]"); 215 | 216 | if ( is_method("ACK") ) { 217 | # ACK is forwarded statelessy 218 | route(NATMANAGE); 219 | } 220 | route(RELAY); 221 | } else { 222 | if ( is_method("ACK") ) { 223 | if ( t_check_trans() ) { 224 | # no loose-route, but stateful ACK; 225 | # must be an ACK after a 487 226 | # or e.g. 404 from upstream server 227 | route(RELAY); 228 | exit; 229 | } else { 230 | # ACK without matching transaction ... ignore and discard 231 | exit; 232 | } 233 | } 234 | sl_send_reply("404","Not here"); 235 | } 236 | exit; 237 | } 238 | } 239 | 240 | # Handle SIP registrations 241 | route[REGISTRAR] { 242 | if (is_method("REGISTER")) { 243 | if(isflagset(FLT_NATS)) { 244 | setbflag(FLB_NATB); 245 | # uncomment next line to do SIP NAT pinging 246 | setbflag(FLB_NATSIPPING); 247 | } 248 | if (!save("location")) 249 | sl_reply_error(); 250 | 251 | exit; 252 | } 253 | } 254 | 255 | # USER location service 256 | route[LOCATION] { 257 | if (!lookup("location")) { 258 | $var(rc) = $rc; 259 | t_newtran(); 260 | switch ($var(rc)) { 261 | case -1: 262 | case -3: 263 | send_reply("404", "Not Found"); 264 | exit; 265 | case -2: 266 | send_reply("405", "Method Not Allowed"); 267 | exit; 268 | } 269 | } 270 | 271 | route(RELAY); 272 | exit; 273 | } 274 | 275 | 276 | # Authentication route 277 | route[AUTH] { 278 | #!ifdef WITH_AUTH 279 | if (is_method("REGISTER") || from_uri==myself) { 280 | # authenticate requests 281 | if (!auth_check("$fd", "subscriber", "1")) { 282 | auth_challenge("$fd", "0"); 283 | exit; 284 | } 285 | # user authenticated - remove auth header 286 | if(!is_method("REGISTER|PUBLISH")) 287 | consume_credentials(); 288 | } 289 | # if caller is not local subscriber, then check if it calls 290 | # a local destination, otherwise deny, not an open relay here 291 | if (from_uri!=myself && uri!=myself) { 292 | sl_send_reply("403","Not relaying"); 293 | exit; 294 | } 295 | 296 | #!endif 297 | return; 298 | } 299 | 300 | # Caller NAT detection route 301 | route[NATDETECT] { 302 | force_rport(); 303 | # if (nat_uac_test("64")) { 304 | if (is_method("REGISTER")) { 305 | fix_nated_register(); 306 | } else { 307 | add_contact_alias(); 308 | # fix_nated_contact(); 309 | } 310 | setflag(FLT_NATS); 311 | # } 312 | return; 313 | } 314 | 315 | # RTPProxy control 316 | route[NATMANAGE] { 317 | if (is_request()) { 318 | if(has_totag()) { 319 | if(check_route_param("nat=yes")) { 320 | setbflag(FLB_NATB); 321 | } 322 | } 323 | } 324 | 325 | 326 | if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) 327 | return; 328 | 329 | if (is_request()) { 330 | if (!has_totag()) { 331 | if(t_is_branch_route()) { 332 | add_rr_param(";nat=yes"); 333 | } 334 | } 335 | } 336 | if (is_reply()) { 337 | if(isbflagset(FLB_NATB)) { 338 | add_contact_alias(); 339 | } 340 | } 341 | } 342 | 343 | # URI update for dialog requests 344 | route[DLGURI] { 345 | if(!isdsturiset()) { 346 | handle_ruri_alias(); 347 | } 348 | return; 349 | } 350 | 351 | onreply_route[REPLY_TO_WS] { 352 | 353 | xlog("L_INFO", "Reply from softphone: $rs"); 354 | 355 | if (t_check_status("183")) { 356 | change_reply_status("180", "Ringing"); 357 | remove_body(); 358 | exit; 359 | } 360 | 361 | if(!(status=~"[12][0-9][0-9]")) 362 | return; 363 | 364 | # rtpengine_manage("froc+SP"); 365 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=force RTP/SAVPF"); 366 | 367 | route(NATMANAGE); 368 | } 369 | 370 | onreply_route[REPLY_FROM_WS] { 371 | 372 | xlog("L_INFO", "Reply from webrtc client: $rs"); 373 | 374 | if(status=~"[12][0-9][0-9]") { 375 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=remove RTP/AVP"); 376 | # rtpengine_manage("froc-sp"); 377 | route(NATMANAGE); 378 | } 379 | } 380 | 381 | onreply_route[MANAGE_CLASSIC_REPLY] { 382 | xlog("L_INFO", "Boring reply from softphone: $rs"); 383 | 384 | if(status=~"[12][0-9][0-9]") { 385 | rtpengine_manage("replace-origin replace-session-connection"); 386 | # rtpengine_manage("co"); 387 | route(NATMANAGE); 388 | } 389 | } 390 | 391 | onreply_route[REPLY_WS_TO_WS] { 392 | xlog("L_INFO", "WS to WS"); 393 | if(status=~"[12][0-9][0-9]") { 394 | rtpengine_manage("force trust-address replace-origin replace-session-connection ICE=force"); 395 | route(NATMANAGE); 396 | } 397 | } 398 | 399 | # manage failure routing cases 400 | failure_route[MANAGE_FAILURE] { 401 | route(NATMANAGE); 402 | 403 | if (t_is_canceled()) { 404 | exit; 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /modules.cfg: -------------------------------------------------------------------------------- 1 | #!KAMAILIO 2 | 3 | #!ifdef WITH_DEBUG 4 | debug=4 5 | log_stderror=yes 6 | #!else 7 | debug=2 8 | log_stderror=no 9 | #!endif 10 | 11 | memdbg=5 12 | memlog=5 13 | 14 | log_facility=LOG_LOCAL0 15 | 16 | fork=yes 17 | children=4 18 | 19 | /* uncomment the next line to disable TCP (default on) */ 20 | #disable_tcp=yes 21 | 22 | /* uncomment the next line to disable the auto discovery of local aliases 23 | based on reverse DNS on IPs (default on) */ 24 | #auto_aliases=no 25 | 26 | #!ifdef WITH_TLS 27 | enable_tls=yes 28 | #!endif 29 | 30 | # life time of TCP connection when there is no traffic 31 | # - a bit higher than registration expires to cope with UA behind NAT 32 | tcp_connection_lifetime=3605 33 | tcp_accept_no_cl=yes 34 | tcp_rd_buf_size=16384 35 | 36 | ####### Modules Section ######## 37 | 38 | # set paths to location of modules (to sources or installation folders) 39 | #!ifdef WITH_SRCPATH 40 | mpath="modules" 41 | #!else 42 | mpath="/usr/local/lib64/kamailio/modules/" 43 | #!endif 44 | 45 | loadmodule "db_mysql.so" 46 | loadmodule "textopsx.so" 47 | 48 | loadmodule "websocket.so" 49 | loadmodule "xhttp.so" 50 | 51 | loadmodule "mi_fifo.so" 52 | loadmodule "kex.so" 53 | loadmodule "corex.so" 54 | loadmodule "tm.so" 55 | loadmodule "tmx.so" 56 | loadmodule "sl.so" 57 | loadmodule "rr.so" 58 | loadmodule "pv.so" 59 | loadmodule "maxfwd.so" 60 | loadmodule "usrloc.so" 61 | loadmodule "registrar.so" 62 | loadmodule "textops.so" 63 | loadmodule "siputils.so" 64 | loadmodule "xlog.so" 65 | loadmodule "sanity.so" 66 | loadmodule "ctl.so" 67 | loadmodule "cfg_rpc.so" 68 | loadmodule "mi_rpc.so" 69 | loadmodule "sdpops.so" 70 | 71 | #!ifdef WITH_AUTH 72 | loadmodule "auth.so" 73 | loadmodule "auth_db.so" 74 | #!endif 75 | 76 | #!ifdef WITH_NAT 77 | loadmodule "nathelper.so" 78 | loadmodule "rtpengine.so" 79 | #!endif 80 | 81 | #!ifdef WITH_TLS 82 | loadmodule "tls.so" 83 | #!endif 84 | 85 | #!ifdef WITH_DEBUG 86 | loadmodule "debugger.so" 87 | #!endif 88 | 89 | # ----------------- setting module-specific parameters --------------- 90 | 91 | 92 | # ----- mi_fifo params ----- 93 | modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") 94 | 95 | 96 | # ----- tm params ----- 97 | # auto-discard branches from previous serial forking leg 98 | modparam("tm", "failure_reply_mode", 3) 99 | # default retransmission timeout: 30sec 100 | modparam("tm", "fr_timer", 30000) 101 | # default invite retransmission timeout after 1xx: 120sec 102 | modparam("tm", "fr_inv_timer", 120000) 103 | 104 | 105 | # ----- rr params ----- 106 | # add value to ;lr param to cope with most of the UAs 107 | modparam("rr", "enable_full_lr", 1) 108 | # do not append from tag to the RR (no need for this script) 109 | modparam("rr", "append_fromtag", 0) 110 | 111 | modparam("corex", "alias_subdomains", "MY_DOMAIN") 112 | 113 | # ----- registrar params ----- 114 | modparam("registrar", "method_filtering", 1) 115 | /* uncomment the next line to disable parallel forking via location */ 116 | # modparam("registrar", "append_branches", 0) 117 | /* uncomment the next line not to allow more than 10 contacts per AOR */ 118 | #modparam("registrar", "max_contacts", 10) 119 | # max value for expires of registrations 120 | modparam("registrar", "max_expires", 3600) 121 | # set it to 1 to enable GRUU 122 | modparam("registrar", "gruu_enabled", 0) 123 | 124 | # ----- usrloc params ----- 125 | /* enable DB persistency for location entries */ 126 | modparam("usrloc", "db_url", DBURL) 127 | modparam("usrloc", "db_mode", 1) 128 | 129 | # ----- auth_db params ----- 130 | #!ifdef WITH_AUTH 131 | modparam("auth_db", "db_url", DBURL) 132 | modparam("auth_db", "calculate_ha1", yes) 133 | modparam("auth_db", "password_column", "password") 134 | modparam("auth_db", "load_credentials", "") 135 | modparam("auth_db", "use_domain", MULTIDOMAIN) 136 | #!endif 137 | 138 | 139 | #!ifdef WITH_NAT 140 | # ----- rtpproxy params ----- 141 | modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:22222") 142 | 143 | # ----- nathelper params ----- 144 | modparam("nathelper", "natping_interval", 15) 145 | modparam("nathelper", "ping_nated_only", 1) 146 | modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) 147 | modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org") 148 | 149 | # params needed for NAT traversal in other modules 150 | modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") 151 | modparam("usrloc", "nat_bflag", FLB_NATB) 152 | #!endif 153 | 154 | 155 | #!ifdef WITH_TLS 156 | # ----- tls params ----- 157 | modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg") 158 | #!endif 159 | 160 | #!ifdef WITH_DEBUG 161 | # ----- debugger params ----- 162 | modparam("debugger", "cfgtrace", 1) 163 | #!endif 164 | 165 | -------------------------------------------------------------------------------- /run-rtpproxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | killall rtpengine 3 | echo 'del 0' > /proc/mediaproxy/control 4 | rtpengine --ip 192.168.2.103 -a 186.0.191.55 --listen-ng=127.0.0.1:22222 -m 30000 -M 35000 5 | --------------------------------------------------------------------------------