# TODO (Future possible improvements):
# ---------------------------------------
# * protocol tuning
-# - session-timer (port existing textops-based scripts)
# - AVP-based diversion for call-forwarding (as opposed to specialized module)
# - add Date header in 200s to REGISTERs (to be packaged with NTP!)
# * more security:
#tcp_max_connections=10240 # default is 2048
tcp_connect_timeout=1
+# -------------------- custom parameters ----------------------------
+# These parameters can be modified runtime via RPC interface,
+# read the documentation of cfg_rpc module!
+#
+# Session Timer parameters, RFC 4028
+#
+# default session interval value used by the proxy if the UAC does not support
+# session timer. Set it to "0" to disable session timer proxy support
+session_timer.default = "1800" desc "default session interval (in s)"
+#
+# minimum session interval value accepted by the proxy,
+# it must not be less than 90 seconds
+session_timer.min_se = "90" desc "minimum session interval (in s)"
+
# ------------------ module loading ----------------------------------
loadmodule "/usr/lib/ser/modules/timer.so"
loadmodule "/usr/lib/ser/modules/db_ops.so"
loadmodule "/usr/lib/ser/modules/exec.so"
+loadmodule "/usr/lib/ser/modules/cfg_rpc.so"
+loadmodule "/usr/lib/ser/modules/eval.so"
# ----------------- setting script FLAGS -----------------------------
flags
FLAG_DONT_RM_CRED : 7, # do not remove the credentials
FLAG_AUTH_OK : 8, # authentication succeeded
FLAG_SERWEB_RSVD1 : 9, # bit reserved for use with serweb
- FLAG_SERWEB_RSVD2 :10; # bit reserved for use with serweb
+ FLAG_SERWEB_RSVD2 : 10, # bit reserved for use with serweb
+ FLAG_SESSIONTIMER : 11, # indicates that the UAC supports Session Timer
+ FLAG_RR_DONE : 12; # the request got already one RR header
avpflags
dialog_cookie; # handled by rr module
modparam("rr", "enable_full_lr", 1)
#
# limit the length of the AVP cookie to only necessary ones
-modparam("rr", "cookie_filter", "(account|uac_nat)")
+modparam("rr", "cookie_filter", "(account|uac_nat|stimer)")
#
# you probably do not want that someone can simply read and change
# the AVP cookie in your Routes, thus should really change this
# bypass the rest of the script for CANCELs if possible
route(CATCH_CANCEL);
- # check if the request is routed via Route header or
- # needs a Record-Route header
- route(RR);
+ # check if the request is routed via Route header
+ route(LOOSE_ROUTE);
# look up domain IDs
route(DOMAIN);
route[FORWARD]
{
- # here you could decide wether this call needs a RTP relay or not
-
# if this is called from the failure route we need to open a new branch
if (isflagset(FLAG_FAILUREROUTE)) {
append_branch();
t_on_failure("FAILURE_ROUTE");
}
+ # always use the reply route to check for NATed UAS
t_on_reply("REPLY_ROUTE");
+ # insert a Record-Route header into all requests
+ # this has to be done as one of the last steps to include all the RR
+ # cookies which might have been created during the script run
+ route(RR);
+
+ # turn on or off the RTP proxy as the last step because it modifies the SDP
route(RTPPROXY);
if (nat_uac_test("19") || (@hf_value["contact"] && @contact.uri.params.maddr) ) {
setflag(FLAG_NAT);
$uac_nat=1;
+ setavpflag($uac_nat, "dialog_cookie");
if (method=="REGISTER") {
# prepare the Contact so that the registrar module saves the
# source as well
break;
}
- # turn the RTP proxy on for INVITEs
- if (method=="INVITE") {
+ # turn the RTP proxy on for INVITEs and UPDATEs
+ if ((method=="INVITE" || method == "UPDATE") && @msg.body != "") {
force_rtp_proxy('r');
append_hf("P-RTP-Proxy: YES\r\n");
}
}
-route[RR]
+route[LOOSE_ROUTE]
{
# subsequent messages withing a dialog should take the
- # path determined by record-routing
+ # path determined by the Route header
if (loose_route()) {
# mark routing logic in request
append_hf("P-hint: rr-enforced\r\n");
setflag(FLAG_NAT);
}
+ # restore Session Timer flag and headers
+ if ($stimer && ($stimer != "0")) {
+ route(SESSION_TIMER);
+ }
+
# for broken devices which overwrite their Route's with each
# (not present) RR from within dialog requests it is better
# to repeat the RRing
# and if we call rr after loose_route the AVP cookies are restored
# automatically :)
+ # There is also one scenario where subsequent indialog RR is
+ # required if the initial SUBSCRIBE forked.
# note that here we forward before authentication check is executed;
# generally we only authenticate dialog-initiating requests; some
# in-dialog requests can't be authenticated at all, see the
# call-forwarding example in route[DOMAIN]
- record_route();
+ route(RR);
route(FORWARD);
- } else if (!method=="REGISTER") {
+ }
+}
+
+route[RR]
+{
+ if (!isflagset(FLAG_RR_DONE) && !method=="REGISTER") {
# we record-route all messages -- to make sure that
# subsequent messages will go through our proxy; that's
# particularly good if upstream and downstream entities
setavpflag($account, "dialog_cookie");
}
+ # let's insert the RR header now
record_route();
+
+ # this flag simply allows to call this route several times
+ # without inserting several RR headers
+ setflag(FLAG_RR_DONE);
}
}
}
}
+ # this enables session timer support as long as one side supports it.
+ # if you want to have session timmer support only for calls from your
+ # PSTN gateway but between pure VoIP calls you can remove the comment
+ # marks from the if clause in the next line and closing bracket below
+ # WARNING: if at all you should trust IP addresses only in your local network!!!
+ #if (@src.ip == $gw_ip) {
+ route(SESSION_TIMER);
+ #}
+
route(FORWARD);
} else {
sl_reply("480", "User temporarily not available");
replace_attr_hf("Remote-Party-ID", "$rpidheader");
}
+ # enable Session Timer support with the GW
+ route(SESSION_TIMER);
+
# just replace the domain part of the RURI with the
# value from the AVP and send it out
attr2uri("$gw_ip", "domain");
if (method == CANCEL) {
# ser 2.1 only
- #if (!t_relay_cancel()) { # implicit drop if the INVITE was found
+ if (!t_relay_cancel()) { # implicit drop if the INVITE was found
# INVITE was found but some error occurred
- # sl_reply("500", "Internal Server Error");
- # drop;
- #}
+ sl_reply("500", "Internal Server Error");
+ drop;
+ }
# bad luck, no corresponding INVITE was found,
# we have to continue with the script
;
}
}
+route[SESSION_TIMER]
+{
+ # we are only interested in session establishment or
+ # session refresher
+ if (!(method == "INVITE" || method == "UPDATE")) {
+ break;
+ }
+
+ # lets check if the Session-Expires header is already present
+ if (@hf_value.session_expires) {
+ # compare the Session-Expires header value with the
+ # configured Min-SE
+ eval_push("x:%@hf_value.session_expires.uri");
+ eval_oper("(int)", -1);
+ eval_push("x:%@cfg_get.session_timer.min_se");
+ eval_oper("(int)", -1);
+ eval_oper(">", -2);
+
+ # lets check for the Suported header
+ if (hf_value_exists("Supported", "timer"))
+ # the UAC supports Session Timer, so we
+ # only need to take a look at the values
+
+ if (@eval.pop[-1] == "0") {
+ # session interval is lower than the configured Min-SE
+ append_to_reply("Min-SE: %@cfg_get.session_timer.min_se\r\n");
+ sl_reply("422", "Session Interval Too Small");
+ drop;
+ }
+
+ # we store the session expires value for the reply route
+ $stimer = @hf_value.session_expires.uri;
+ # and mark the AVP to be inserted as RR cookie
+ setavpflag($stimer, "dialog_cookie");
+
+ # set the session timer flag that indicates the
+ # UAC supports the extension.
+ setflag(FLAG_SESSIONTIMER);
+ } else {
+ #session epxires was already inserted by some other proxy
+ if (@eval.pop[-1] == "0") {
+ # session interval is lower than the configured Min-SE.
+ # There is no point in sending 422 response, because the UAC
+ # does not support the extension, the values can be corrected instead.
+ assign_hf_value("Session-Expires", "%@cfg_get.session_timer.min_se");
+ remove_hf_value("Min-SE");
+ append_hf_value("Min-SE", "%@cfg_get.session_timer.min_se");
+ }
+ }
+ } else {
+ # no Session Timer is requested yet, neither by UAC nor by proxy
+
+ if (@cfg_get.session_timer.default != "0") {
+ # Add a Session Expires header
+ # to see if the UAS supports Session Timer.
+ # We do not insert a Required header because then the
+ # call might fail.
+ append_hf_value("Session-Expires", "%@cfg_get.session_timer.default");
+ if (@cfg_get.session_timer.min_se != "90") { # not the default value
+ append_hf_value("Min-SE", "%@cfg_get.session_timer.min_se");
+ }
+
+ # mark the AVP to be inserted as RR cookie
+ $stimer = @cfg_get.session_timer.default;
+ setavpflag($stimer, "dialog_cookie");
+ }
+ }
+}
failure_route[FAILURE_ROUTE]
{
# which contains a body, start to use the RTP proxy
if (isflagset(FLAG_NAT) &&
status=~"(18[03])|(2[0-9][0-9])" &&
- !search("^(Content-Length|l): 0")) {
+ @msg.body != "") {
force_rtp_proxy('r');
}
+
+ # lets check for session timer support
+ if (isflagset(FLAG_SESSIONTIMER) && status =~ "2[0-9][0-9]") {
+ # the UAC wanted to have a session timer
+
+ if (!@hf_value.session_expires) {
+ # but the UAS does not support it
+ # so we will try to convince the UAC to do it
+ append_hf_value("Session-Expires", "%$stimer;refresher=uac");
+
+ if (!hf_value_exists("Require", "timer")) {
+ include_hf_value("Require", "timer");
+ }
+ }
+ }
}