asterisk: test Precondition extension in IMS MO call

Related: SYS#6969
Change-Id: I1e26f3bb9e54be5b5e15a003b2000ae3e88b9027
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn
index 05b1638..981d5f1 100644
--- a/asterisk/Asterisk_Tests.ttcn
+++ b/asterisk/Asterisk_Tests.ttcn
@@ -49,7 +49,7 @@
 	charstring mp_ami_user := "test_user";
 	charstring mp_ami_secret := "1234";
 	charstring mp_volte_ims_outbound_registration := "volte_ims";
-	/* Current default by pjproject (timeout_timer_val) to 32s, and not changed by Asterisk */
+	/* Current default by pjproject (timeout_timer_val) is set to 32s, and not changed by Asterisk */
 	integer mp_volte_ims_outbound_register_timeout := 32;
 }
 
@@ -561,8 +561,8 @@
 					      true, false);
 }
 
-/* Test SIP registration of local clients */
-private function f_TC_ims_call_mo(charstring id) runs on IMS_ConnHdlr {
+/* Test IMS MO call emulating an MT which doesn't support precondition */
+private function f_TC_ims_call_mo_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr {
 	f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
 	if (ispresent(g_pars.subscr.cp.called)) {
 		f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)));
@@ -576,7 +576,7 @@
 	setverdict(pass);
 	as_IMS_unregister();
 }
-testcase TC_ims_call_mo() runs on test_CT {
+private function f_TC_ims_call_mo(boolean use_precondition_ext) runs on test_CT {
 	var SIPConnHdlrPars sip_pars;
 	var IMS_ConnHdlrPars ims_pars;
 	var SIPConnHdlr vc_conn_sip;
@@ -595,8 +595,10 @@
 							 ts_UserInfo(ims_pars.subscr.msisdn)));
 	ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
 							ts_UserInfo(c_ext_msisdn)));
+	ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext;
+	ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext;
 
-	vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mo), ims_pars);
+	vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mo_IMS_ConnHdlr), ims_pars);
 	vc_conn_sip := f_start_handler(refers(f_TC_internal_call_mo), sip_pars);
 
 	COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip;
@@ -623,6 +625,12 @@
 	vc_conn_ims.done;
 	f_shutdown();
 }
+testcase TC_ims_call_mo() runs on test_CT {
+	f_TC_ims_call_mo(true);
+}
+testcase TC_ims_call_mo_noprecondition() runs on test_CT {
+	f_TC_ims_call_mo(false);
+}
 
 /* Test SIP registration of local clients */
 private function f_TC_ims_call_mt(charstring id) runs on IMS_ConnHdlr {
@@ -706,6 +714,7 @@
 	execute( TC_ims_registration_timeout_protected_100Trying() );
 	execute( TC_ims_registration_timeout_protected_200OK() );
 	execute( TC_ims_call_mo() );
+	execute( TC_ims_call_mo_noprecondition() );
 	execute( TC_ims_call_mt() );
 }
 
diff --git a/asterisk/IMS_ConnectionHandler.ttcn b/asterisk/IMS_ConnectionHandler.ttcn
index 4f3cbb7..860b799 100644
--- a/asterisk/IMS_ConnectionHandler.ttcn
+++ b/asterisk/IMS_ConnectionHandler.ttcn
@@ -138,6 +138,8 @@
 	charstring local_rtp_addr,
 	uint16_t local_rtp_port,
 
+	boolean support_precondition_ext,
+	boolean require_precondition_ext,
 	SDP_Message peer_sdp optional,
 	IMS_CallParsMT mt
 }
@@ -155,6 +157,8 @@
 	sip_body := omit,
 	local_rtp_addr := local_rtp_addr,
 	local_rtp_port := local_rtp_port,
+	support_precondition_ext := true,
+	require_precondition_ext := false,
 	peer_sdp := omit,
 	mt := t_IMS_CallParsMT
 }
@@ -1104,24 +1108,106 @@
 		var template (value) PDU_SIP_Response tx_resp;
 		var Via via;
 		var charstring tx_sdp;
+		var boolean peer_support_precondition := match(g_rx_sip_req.msgHeader.supported.optionsTags,
+							       superset("100rel", "precondition"));
 
 		/* Obtain params: */
 		f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
 		via := g_rx_sip_req.msgHeader.via;
 
 		f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := true);
+		if (g_pars.subscr.cp.require_precondition_ext and not peer_support_precondition) {
+			Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+						log2str(g_name & ": Missing '100rel,precondition' in INVITE Supported header: ",
+						g_rx_sip_req.msgHeader.supported));
+		}
 
-
-		/* Tx 180 Ringing */
-		tx_resp := ts_SIP_Response_Ringing(g_pars.subscr.cp.sip_call_id,
+		/* Tx 100 Tyring */
+		tx_resp := ts_SIP_Response_Trying(g_pars.subscr.cp.sip_call_id,
 						   g_pars.subscr.cp.from_addr,
 						   g_pars.subscr.cp.to_addr,
 						   via,
-						   g_pars.subscr.cp.sip_seq_nr);
+						   g_pars.subscr.cp.sip_seq_nr,
+						   "INVITE",
+						   allow := omit,
+						   server := g_pars.server_name,
+						   userAgent := omit);
 		SIP.send(tx_resp);
 
-		/* Tx 200 OK */
 		tx_sdp := f_gen_sdp();
+
+		/* Use precondition ? */
+		if (g_pars.subscr.cp.require_precondition_ext or
+		    (g_pars.subscr.cp.support_precondition_ext and peer_support_precondition)) {
+			/* Tx 183 Session Progress */
+			/* TODO: add a=curr:qos, a=des:qos and a=conf:qos fields to SDP */
+			tx_resp := ts_SIP_Response_SessionProgress(g_pars.subscr.cp.sip_call_id,
+							g_pars.subscr.cp.from_addr,
+							g_pars.subscr.cp.to_addr,
+							via,
+							g_pars.subscr.cp.sip_seq_nr,
+							rseq := ts_RSeq(1),
+							body := tx_sdp);
+			SIP.send(tx_resp);
+
+			/* Rx PRACK */
+			exp_req := tr_SIP_PRACK(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)),
+					g_pars.subscr.cp.sip_call_id,
+					g_pars.subscr.cp.from_addr,
+					g_pars.subscr.cp.to_addr,
+					f_tr_Via_response(via),
+					g_pars.subscr.cp.sip_seq_nr + 1,
+					rack := tr_RAck(1, g_pars.subscr.cp.sip_seq_nr, "INVITE"),
+					body := omit);
+			as_SIP_expect_req(exp_req);
+
+			/* Tx 200 OK (PRACK) */
+			tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id,
+						g_pars.subscr.cp.from_addr,
+						g_pars.subscr.cp.to_addr,
+						"PRACK", 200,
+						g_pars.subscr.cp.sip_seq_nr,
+						"OK",
+						via,
+						body := omit);
+			SIP.send(tx_resp);
+
+			/* Rx UPDATE */
+			exp_req := tr_SIP_UPDATE(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)),
+					g_pars.subscr.cp.sip_call_id,
+					g_pars.subscr.cp.from_addr,
+					g_pars.subscr.cp.to_addr,
+					f_tr_Via_response(via),
+					g_pars.subscr.cp.sip_seq_nr + 1,
+					require := tr_Require(superset("precondition")),
+					body := ?);
+			as_SIP_expect_req(exp_req);
+
+			/* Tx 200 OK (UPDATE) */
+			/* TODO: add a=curr:qos, a=des:qos and a=conf:qos fields to SDP */
+			tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id,
+						g_pars.subscr.cp.from_addr,
+						g_pars.subscr.cp.to_addr,
+						"UPDATE", 200,
+						g_pars.subscr.cp.sip_seq_nr,
+						"OK",
+						via,
+						body := tx_sdp);
+			SIP.send(tx_resp);
+
+			g_pars.subscr.cp.sip_seq_nr := g_pars.subscr.cp.sip_seq_nr + 1;
+		}
+
+		/* Tx 180 Ringing */
+		tx_resp := ts_SIP_Response_Ringing(g_pars.subscr.cp.sip_call_id,
+						g_pars.subscr.cp.from_addr,
+						g_pars.subscr.cp.to_addr,
+						via,
+						g_pars.subscr.cp.sip_seq_nr);
+		SIP.send(tx_resp);
+
+
+		/* Tx 200 OK */
 		tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id,
 					   g_pars.subscr.cp.from_addr,
 					   g_pars.subscr.cp.to_addr,
diff --git a/asterisk/expected-results.xml b/asterisk/expected-results.xml
index 6b5def5..e9d2b82 100644
--- a/asterisk/expected-results.xml
+++ b/asterisk/expected-results.xml
@@ -13,5 +13,6 @@
   <testcase classname='Asterisk_Tests' name='TC_ims_registration_timeout_protected_100Trying' time='MASKED'/>
   <testcase classname='Asterisk_Tests' name='TC_ims_registration_timeout_protected_200OK' time='MASKED'/>
   <testcase classname='Asterisk_Tests' name='TC_ims_call_mo' time='MASKED'/>
+  <testcase classname='Asterisk_Tests' name='TC_ims_call_mo_noprecondition' time='MASKED'/>
   <testcase classname='Asterisk_Tests' name='TC_ims_call_mt' time='MASKED'/>
 </testsuite>
diff --git a/library/SIP_Templates.ttcn b/library/SIP_Templates.ttcn
index 77d4ee0..cfe21eb 100644
--- a/library/SIP_Templates.ttcn
+++ b/library/SIP_Templates.ttcn
@@ -284,6 +284,24 @@
 	p_assoc_uris := p_assoc_uris
 }
 
+// RFC 3262
+template (value) RAck ts_RAck(template (value) integer response_num,
+			      template (value) integer seq_nr,
+			      template (value) charstring method := "INVITE") := {
+	fieldName := RACK_E,
+	response_num := response_num,
+	seqNumber := seq_nr,
+	method := method
+}
+template (present) RAck tr_RAck(template (present) integer response_num := ?,
+			        template (present) integer seq_nr := ?,
+			        template (present) charstring method := ?) := {
+	fieldName := RACK_E,
+	response_num := response_num,
+	seqNumber := seq_nr,
+	method := method
+}
+
 // [20.32]
 template (value) Require ts_Require(template (value) OptionTag_List optionsTags := {}) := {
 	fieldName := REQUIRE_E,
@@ -294,6 +312,16 @@
 	optionsTags := optionsTags
 }
 
+// RFC 3262
+template (value) RSeq ts_RSeq(template (value) integer response_num) := {
+	fieldName := RSEQ_E,
+	response_num := response_num
+}
+template (present) RSeq tr_RSeq(template (present) integer response_num := ?) := {
+	fieldName := RSEQ_E,
+	response_num := response_num
+}
+
 // [20.35 RFC2616 14.38]
 template (value) Server ts_Server(template (value) ServerVal_List serverBody := {}) := {
 	fieldName := SERVER_E,
@@ -652,7 +680,9 @@
 		template (omit) Allow allow := ts_Allow(c_SIP_defaultMethods),
 		template (omit) Expires expires := omit,
 		template (omit) P_Associated_Uri p_associated_uri := omit,
+		template (omit) RAck rack := omit,
 		template (omit) Require require := omit,
+		template (omit) RSeq rseq := omit,
 		template (omit) Security_client security_client := omit,
 		template (omit) Security_server security_server := omit,
 		template (omit) Server server := omit,
@@ -677,7 +707,9 @@
 	expires := expires,
 	fromField := from_addr,
 	p_associated_uri := p_associated_uri,
+	rack := rack,
 	require := require,
+	rseq := rseq,
 	security_client := security_client,
 	security_server := security_server,
 	server := server,
@@ -702,6 +734,7 @@
 		template Authorization authorization := *,
 		template Expires expires := *,
 		template P_Associated_Uri p_associated_uri := *,
+		template RAck rack := *,
 		template Require require := *,
 		template Security_client security_client := *,
 		template Security_server security_server := *,
@@ -727,6 +760,7 @@
 	expires := expires,
 	fromField := from_addr,
 	p_associated_uri := p_associated_uri,
+	rack := rack,
 	require := require,
 	security_client := security_client,
 	security_server := security_server,
@@ -905,6 +939,42 @@
 	payload := omit
 }
 
+template (present) PDU_SIP_Request
+tr_SIP_PRACK(template (present) SipUrl uri,
+	     template CallidString call_id,
+	     template From from_addr,
+	     template To to_addr,
+	     template Via via,
+	     template integer seq_nr,
+	     template RAck rack := tr_RAck(?, ?, ?),
+	     template charstring body := *) := {
+	requestLine := tr_SIP_ReqLine(PRACK_E, uri),
+	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
+				     via,
+				     "PRACK", seq_nr,
+				     rack := rack),
+	messageBody := body,
+	payload := omit
+}
+
+template (present) PDU_SIP_Request
+tr_SIP_UPDATE(template (present) SipUrl uri,
+	      template CallidString call_id,
+	      template From from_addr,
+	      template To to_addr,
+	      template Via via,
+	      template integer seq_nr,
+	      template Require require := *,
+	      template charstring body) := {
+	requestLine := tr_SIP_ReqLine(UPDATE_E, uri),
+	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
+				     via,
+				     "UPDATE", seq_nr,
+				     require := require),
+	messageBody := body,
+	payload := omit
+}
+
 template (value) PDU_SIP_Response
 ts_SIP_Response(template (value) CallidString call_id,
 		template (value) From from_addr,
@@ -979,6 +1049,29 @@
 	payload := omit
 }
 
+/* 183 Session Progress */
+template (value) PDU_SIP_Response
+ts_SIP_Response_SessionProgress(
+	template (value) CallidString call_id,
+	template (value) From from_addr,
+	template (value) To to_addr,
+	Via via,
+	integer seq_nr,
+	charstring method := "INVITE",
+	template (omit) Require require := ts_Require({"100rel", "precondition"}),
+	template (omit) RSeq rseq := ts_RSeq(1),
+	template (omit) charstring body := omit) := {
+	statusLine := ts_SIP_StatusLine(183, "Session Progress"),
+	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
+				     via,
+				     content_length := f_ContentLength(body),
+				     content_type := f_ContentTypeOrOmit(ts_CT_SDP, body),
+				     require := require,
+				     rseq := rseq),
+	messageBody := body,
+	payload := omit
+}
+
 /* 401 Unauthorized */
 template (value) PDU_SIP_Response
 ts_SIP_Response_Unauthorized(