Explorar o código

- Session Timer proxy support is introduced (RFC 4028)
- 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)

Miklos Tirpak %!s(int64=17) %!d(string=hai) anos
pai
achega
6d4d32e322
Modificáronse 1 ficheiros con 153 adicións e 19 borrados
  1. 153 19
      etc/ser-oob.cfg

+ 153 - 19
etc/ser-oob.cfg

@@ -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");
+			}
+		}
+	}
 }