瀏覽代碼

- 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 17 年之前
父節點
當前提交
6d4d32e322
共有 1 個文件被更改,包括 153 次插入19 次删除
  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");
+			}
+		}
+	}
 }