- Session Timer proxy support is introduced (RFC 4028)
authorMiklos Tirpak <miklos@iptel.org>
Tue, 18 Mar 2008 17:04:16 +0000 (17:04 +0000)
committerMiklos Tirpak <miklos@iptel.org>
Tue, 18 Mar 2008 17:04:16 +0000 (17:04 +0000)
- custom config parameters are added to the script
- t_relay_cancel() is enabled to bypass the routing logic if possible
- NAT fixes (patch from Nils Ohlmeier)
- Record routing and loose routing is split into two parts to support
encoding of AVPs that are lately set in the script (patch from
Nils Ohlmeier)

etc/ser-oob.cfg

index c97500b..01dc5a6 100644 (file)
@@ -57,7 +57,6 @@
 # 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: 
@@ -196,6 +195,20 @@ tcp_connection_lifetime=3600
 #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 ----------------------------------
 
@@ -228,6 +241,8 @@ loadmodule "/usr/lib/ser/modules/speeddial.so"
 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
@@ -240,7 +255,9 @@ 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
@@ -302,7 +319,7 @@ modparam("auth", "secret", "aqwedrftredswqwddcft")
 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
@@ -405,9 +422,8 @@ route{
        # 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);
@@ -454,8 +470,6 @@ route{
 
 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();
@@ -467,8 +481,15 @@ route[FORWARD]
                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);
 
 
@@ -552,6 +573,7 @@ route[NAT_DETECTION]
        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
@@ -579,17 +601,17 @@ route[RTPPROXY]
                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"); 
@@ -609,19 +631,31 @@ route[RR]
                        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
@@ -634,7 +668,12 @@ route[RR]
                        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);
        }
 }
 
@@ -914,6 +953,15 @@ route[INBOUND]
                        }
                }
 
+               # 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");
@@ -951,6 +999,9 @@ route[PSTN]
                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");
@@ -965,12 +1016,12 @@ route[CATCH_CANCEL] {
 
        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
                ;
@@ -989,6 +1040,74 @@ route[SITE_SPECIFIC] {
        }
 }
 
+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]
 {
@@ -1038,9 +1157,24 @@ onreply_route[REPLY_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");
+                       }
+               }
+       }
 }