Tutorial Overview
Section titled “Tutorial Overview”The purpose of this module is to help you start in an easy way with the OpenSIPS script. The learning curve gets milder as the beginners can start with a simplified format of the script - the idea is to shift the main focus on routing the SIP initial requests (which define the service logic), while transparently handling a lot of “standard” SIP scripting logic, in order to both save script coding time and mitigate potential SIP scripting errors. This tutorial offers both a brief overview on the features of the new module and an example script.
Current features
Section titled “Current features”As stated in the documentation, the module currently features:
-
transparent handling of SIP sequential requests
- sequential requests are properly and silently handled - they will not hit the script at all anymore
- for initial SIP requests, the module performs record-routing (i.e. OpenSIPS adds itself in the signaling path using a Record-Route header), after which the main route is triggered, just as before
- for sequential SIP requests, the module is automatically doing loose_route() for proper routing.
- optionally a special route may be run just before all sequential requests are routed out
-
automatic dialog support
- automatically create dialog support for the INVITE-based sessions
- for sequential requests within a dialog, a complete dialog matching (Call-ID, From-tag, To-tag) will be attempted if the quick ‘did’-based Record-Route parameter matching fails
Future directions for the module include adding authentication and NAT support.
Example script
Section titled “Example script”SIP logic provided:
- simple standard SIP-2-SIP calling (with registering)
- initial and sequential SIP request routing (using the script helper)
- dialog support (using the script helper)
Scripting examples
Section titled “Scripting examples”This is the OpenSIPS script with the script_helper support.
## OpenSIPS residential configuration script# by OpenSIPS Solutions <team@opensips-solutions.com>## This is a basic residential configuration.## Please refer to the OpenSIPS Manuals at:# https://opensips.org/Documentation/Manuals# for an explanation of available statements, functions and parameters.#
####### Global Parameters #########
/* uncomment the following lines to enable debugging */#debug_mode=yes
log_level=3xlog_level=3stderror_enabled=nosyslog_enabled=yessyslog_facility=LOG_LOCAL0
udp_workers=4
/* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */#disable_dns_blacklist=no
/* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */#dns_try_ipv6=yes
socket=udp:127.0.0.1:5060 # CUSTOMIZE ME
####### Modules Section ########
#set module pathmpath="/usr/local/lib/opensips/modules/"
#### SIGNALING moduleloadmodule "signaling.so"
#### StateLess moduleloadmodule "sl.so"
#### Transaction Moduleloadmodule "tm.so"modparam("tm", "fr_timeout", 5)modparam("tm", "fr_inv_timeout", 30)modparam("tm", "restart_fr_on_each_reply", 0)modparam("tm", "onreply_avp_mode", 1)
#### Record Route Moduleloadmodule "rr.so"/* do not append from tag to the RR (no need for this script) */modparam("rr", "append_fromtag", 0)
#### MAX ForWarD moduleloadmodule "maxfwd.so"
#### SIP MSG OPerationS moduleloadmodule "sipmsgops.so"
#### FIFO Management Interfaceloadmodule "mi_fifo.so"modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")modparam("mi_fifo", "fifo_mode", 0666)
#### USeR LOCation moduleloadmodule "usrloc.so"modparam("usrloc", "nat_bflag", "NAT")modparam("usrloc", "working_mode_preset", "single-instance-no-db")
#### REGISTRAR moduleloadmodule "registrar.so"modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")/* uncomment the next line not to allow more than 10 contacts per AOR */#modparam("registrar", "max_contacts", 10)
#### ACCounting moduleloadmodule "acc.so"/* what special events should be accounted ? */modparam("acc", "early_media", 0)modparam("acc", "report_cancels", 0)/* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure to enable "append_fromtag" in "rr" module */modparam("acc", "detect_direction", 0)
#### DIALOG moduleloadmodule "dialog.so"modparam("dialog", "db_mode", 0) # no DB
#### SCRIPT HELPER moduleloadmodule "script_helper.so"# this route will be run right before sequential requests are routed outmodparam("script_helper", "sequential_route", "my_seq_route")modparam("script_helper", "use_dialog", 1)modparam("script_helper", "create_dialog_flags", "bye-on-timeout,options-ping-caller,options-ping-callee")
loadmodule "proto_udp.so"
####### Routing Logic ########
# main request routing logic
route{
if (!mf_process_maxfwd_header(10)) { send_reply(483,"Too Many Hops"); exit; }
if ( !(is_method("REGISTER") ) ) { if (is_myself("$fd")) {
} else { # if caller is not local, then called number must be local if (!is_myself("$rd")) { send_reply(403,"Relay Forbidden"); exit; } } }
# account only INVITEs if (is_method("INVITE")) { do_accounting("log","cdr"); }
if (!is_myself("$rd")) { append_hf("P-hint: outbound\r\n"); route(relay); }
# requests for my domain if (is_method("PUBLISH|SUBSCRIBE")) { send_reply(503, "Service Unavailable"); exit; }
if (is_method("REGISTER")) { # store the registration and generate a SIP reply if (!save("location")) xlog("failed to register AoR $tu\n");
exit; }
if ($rU==NULL) { # request with no Username in RURI send_reply(484,"Address Incomplete"); exit; }
# do lookup with method filtering if (!lookup("location","method-filtering")) { t_reply(404, "Not Found"); exit; }
# when routing via usrloc, log the missed calls also do_accounting("log","missed"); route(relay);}
route [my_seq_route]{ xlog("---- Sequential request handler ---- \n"); xlog("$rm | Call-ID: $ci | From-tag: $ft | To-tag: $tt\n");}
route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { t_on_branch("per_branch_ops"); t_on_reply("handle_nat"); t_on_failure("missed_call"); }
if (!t_relay()) { send_reply(500,"Internal Error"); } exit;}
branch_route[per_branch_ops] { xlog("new branch at $ru\n");}
onreply_route[handle_nat] { xlog("incoming reply\n");}
failure_route[missed_call] { if (t_was_cancelled()) { exit; }
# uncomment the following lines if you want to block client # redirect based on 3xx replies. ##if (t_check_status("3[0-9][0-9]")) { ##t_reply(404,"Not found"); ## exit; ##}
}You can check it against the regular OpenSIPS script without the script_helper support.
## OpenSIPS residential configuration script# by OpenSIPS Solutions <team@opensips-solutions.com>## This is a basic residential configuration.## Please refer to the OpenSIPS Manuals at:# https://opensips.org/Documentation/Manuals# for an explanation of available statements, functions and parameters.#
####### Global Parameters #########
/* uncomment the following lines to enable debugging */#debug_mode=yes
log_level=3xlog_level=3stderror_enabled=nosyslog_enabled=yessyslog_facility=LOG_LOCAL0
udp_workers=4
/* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */#disable_dns_blacklist=no
/* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */#dns_try_ipv6=yes
socket=udp:127.0.0.1:5060 # CUSTOMIZE ME
####### Modules Section ########
#set module pathmpath="/usr/local/lib/opensips/modules/"
#### SIGNALING moduleloadmodule "signaling.so"
#### StateLess moduleloadmodule "sl.so"
#### Transaction Moduleloadmodule "tm.so"modparam("tm", "fr_timeout", 5)modparam("tm", "fr_inv_timeout", 30)modparam("tm", "restart_fr_on_each_reply", 0)modparam("tm", "onreply_avp_mode", 1)
#### Record Route Moduleloadmodule "rr.so"/* do not append from tag to the RR (no need for this script) */modparam("rr", "append_fromtag", 0)
#### MAX ForWarD moduleloadmodule "maxfwd.so"
#### SIP MSG OPerationS moduleloadmodule "sipmsgops.so"
#### FIFO Management Interfaceloadmodule "mi_fifo.so"modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")modparam("mi_fifo", "fifo_mode", 0666)
#### USeR LOCation moduleloadmodule "usrloc.so"modparam("usrloc", "nat_bflag", "NAT")modparam("usrloc", "working_mode_preset", "single-instance-no-db")
#### REGISTRAR moduleloadmodule "registrar.so"modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")/* uncomment the next line not to allow more than 10 contacts per AOR */#modparam("registrar", "max_contacts", 10)
#### ACCounting moduleloadmodule "acc.so"/* what special events should be accounted ? */modparam("acc", "early_media", 0)modparam("acc", "report_cancels", 0)/* by default we do not adjust the direct of the sequential requests. if you enable this parameter, be sure to enable "append_fromtag" in "rr" module */modparam("acc", "detect_direction", 0)
#### DIALOG moduleloadmodule "dialog.so"modparam("dialog", "db_mode", 0) # no DB
loadmodule "proto_udp.so"
####### Routing Logic ########
# main request routing logic
route{
if (!mf_process_maxfwd_header(10)) { send_reply(483,"Too Many Hops"); exit; }
if (has_totag()) {
# handle hop-by-hop ACK (no routing required) if ( is_method("ACK") && t_check_trans() ) { t_relay(); exit; }
# sequential request within a dialog should # take the path determined by record-routing if ( !loose_route() ) { # we do record-routing for all our traffic, so we should not # receive any sequential requests without Route hdr. send_reply(404,"Not here"); exit; }
if (is_method("BYE")) { # do accounting even if the transaction fails do_accounting("log","failed"); }
# route it out to whatever destination was set by loose_route() # in $du (destination URI). route(relay); exit; }
# CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; }
# absorb retransmissions, but do not create transaction t_check_trans();
if ( !(is_method("REGISTER") ) ) { if (is_myself("$fd")) {
} else { # if caller is not local, then called number must be local if (!is_myself("$rd")) { send_reply(403,"Relay Forbidden"); exit; } } }
# preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) send_reply(403,"Preload Route denied"); exit; }
# record routing if (!is_method("REGISTER|MESSAGE")) record_route();
# account only INVITEs if (is_method("INVITE")) { do_accounting("log","cdr"); }
if (!is_myself("$rd")) { append_hf("P-hint: outbound\r\n"); route(relay); }
# requests for my domain if (is_method("PUBLISH|SUBSCRIBE")) { send_reply(503, "Service Unavailable"); exit; }
if (is_method("REGISTER")) { # store the registration and generate a SIP reply if (!save("location")) xlog("failed to register AoR $tu\n");
exit; }
if ($rU==NULL) { # request with no Username in RURI send_reply(484,"Address Incomplete"); exit; }
# do lookup with method filtering if (!lookup("location","method-filtering")) { t_reply(404, "Not Found"); exit; }
# when routing via usrloc, log the missed calls also do_accounting("log","missed"); route(relay);}
route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { t_on_branch("per_branch_ops"); t_on_reply("handle_nat"); t_on_failure("missed_call"); }
if (!t_relay()) { send_reply(500,"Internal Error"); } exit;}
branch_route[per_branch_ops] { xlog("new branch at $ru\n");}
onreply_route[handle_nat] { xlog("incoming reply\n");}
failure_route[missed_call] { if (t_was_cancelled()) { exit; }
# uncomment the following lines if you want to block client # redirect based on 3xx replies. ##if (t_check_status("3[0-9][0-9]")) { ##t_reply(404,"Not found"); ## exit; ##}
}Scripts differences
Section titled “Scripts differences”--- modules/script_helper/samples/opensips-regular.cfg 2026-07-21 13:11:53.483884563 +0300+++ modules/script_helper/samples/opensips-script-helper.cfg 2026-07-21 13:11:47.175918222 +0300@@ -95,6 +95,13 @@ loadmodule "dialog.so" modparam("dialog", "db_mode", 0) # no DB
+#### SCRIPT HELPER module+loadmodule "script_helper.so"+# this route will be run right before sequential requests are routed out+modparam("script_helper", "sequential_route", "my_seq_route")+modparam("script_helper", "use_dialog", 1)+modparam("script_helper", "create_dialog_flags", "bye-on-timeout,options-ping-caller,options-ping-callee")+ loadmodule "proto_udp.so"
@@ -109,44 +116,6 @@ exit; }
- if (has_totag()) {-- # handle hop-by-hop ACK (no routing required)- if ( is_method("ACK") && t_check_trans() ) {- t_relay();- exit;- }-- # sequential request within a dialog should- # take the path determined by record-routing- if ( !loose_route() ) {- # we do record-routing for all our traffic, so we should not- # receive any sequential requests without Route hdr.- send_reply(404,"Not here");- exit;- }-- if (is_method("BYE")) {- # do accounting even if the transaction fails- do_accounting("log","failed");- }-- # route it out to whatever destination was set by loose_route()- # in $du (destination URI).- route(relay);- exit;- }-- # CANCEL processing- if (is_method("CANCEL")) {- if (t_check_trans())- t_relay();- exit;- }-- # absorb retransmissions, but do not create transaction- t_check_trans();- if ( !(is_method("REGISTER") ) ) { if (is_myself("$fd")) {
@@ -159,19 +128,6 @@ } }
- # preloaded route checking- if (loose_route()) {- xlog("L_ERR",- "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");- if (!is_method("ACK"))- send_reply(403,"Preload Route denied");- exit;- }-- # record routing- if (!is_method("REGISTER|MESSAGE"))- record_route();- # account only INVITEs if (is_method("INVITE")) { do_accounting("log","cdr");@@ -214,6 +170,13 @@ }
+route [my_seq_route]+{+ xlog("---- Sequential request handler ---- \n");+ xlog("$rm | Call-ID: $ci | From-tag: $ft | To-tag: $tt\n");+}++ route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) {