aboutsummaryrefslogtreecommitdiffstats
path: root/asterisk/Asterisk_Tests.ttcn
diff options
context:
space:
mode:
Diffstat (limited to 'asterisk/Asterisk_Tests.ttcn')
-rw-r--r--asterisk/Asterisk_Tests.ttcn1286
1 files changed, 1286 insertions, 0 deletions
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn
new file mode 100644
index 00000000..dccf9fa2
--- /dev/null
+++ b/asterisk/Asterisk_Tests.ttcn
@@ -0,0 +1,1286 @@
+module Asterisk_Tests {
+
+/* Asterisk test suite in TTCN-3
+ * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All rights reserved.
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import from TCCOpenSecurity_Functions all;
+import from General_Types all;
+import from Osmocom_Types all;
+import from Native_Functions all;
+import from Misc_Helpers all;
+import from TELNETasp_PortType all;
+import from AMI_Functions all;
+
+import from SDP_Types all;
+import from SDP_Templates all;
+
+import from SIP_Emulation all;
+import from SIPmsg_Types all;
+import from SIP_Templates all;
+
+import from SIP_ConnectionHandler all;
+import from IMS_ConnectionHandler all;
+
+modulepar {
+ charstring mp_local_sip_host := "127.0.0.2";
+ integer mp_local_sip_port := 5060;
+ charstring mp_remote_sip_host := "127.0.0.1";
+ integer mp_remote_sip_port := 5060;
+
+ charstring mp_local_ims_host := "127.0.0.3";
+ integer mp_local_ims_port := 5060;
+ charstring mp_ims_domain := "ims.mnc001.mcc238.3gppnetwork.org"
+ charstring mp_ims_imsi := "238010000090828";
+ charstring mp_ims_imei := "35876110-027790-0";
+
+ /* Asterisk AMI: */
+ charstring mp_ami_remote_host := "127.0.0.1";
+ integer mp_ami_remote_port := 5038;
+ charstring mp_ami_local_host := "0.0.0.0";
+ integer mp_ami_local_port := 0;
+ 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) is set to 32s, and not changed by Asterisk */
+ integer mp_volte_ims_outbound_register_timeout := 32;
+ /* It can sometimes take up to ~30s for Asterisk to finish startup and sending the FullyBooted event... */
+ float mp_ami_ev_fullybooted_timeout := 30.0;
+}
+
+type component test_CT {
+ /* Manages all local VoIP users Asterisk is serving: */
+ var SIP_Emulation_CT vc_SIP;
+ /* Manages the IMS server Asterisk connects to: */
+ var SIP_Emulation_CT vc_IMS;
+
+ /* Connection towards Asterisk AMI iface: */
+ var AMI_Adapter_CT vc_AMI;
+ port AMI_Msg_PT AMI_CLIENT;
+
+ port Coord_PT COORD;
+ port IMSCoord_PT IMS_COORD;
+
+ /* Store Channel name obtained from Newchannel AMI event: */
+ var charstring g_asterisk_chan_name;
+}
+
+const charstring broadcast_sip_extension := "0500";
+
+
+function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return SIPConnHdlrPars {
+ var template (value) CallPars cp := t_CallPars(mp_local_sip_host, 1234 + 2*idx);
+ var template (value) SIPConnHdlrPars pars := t_Pars(mp_local_sip_host,
+ mp_local_sip_port,
+ mp_remote_sip_host,
+ mp_remote_sip_port,
+ "0" & int2str(str2int(broadcast_sip_extension) + idx),
+ cp := cp);
+ return valueof(pars);
+}
+
+function f_init_IMS_ConnHdlrPars(integer idx := 1) runs on test_CT return IMS_ConnHdlrPars {
+ var template (value) IMS_CallPars cp := t_IMS_CallPars(mp_local_ims_host, 1234 + 2*idx);
+ var template (value) IMS_ConnHdlrPars pars := t_IMS_Pars(mp_local_ims_host,
+ mp_local_ims_port,
+ mp_ims_domain,
+ mp_ims_imsi,
+ mp_ims_imei,
+ cp := cp);
+ return valueof(pars);
+}
+
+/* Initialize connection towards Asterisk AMI */
+private function f_init_ami() runs on test_CT {
+ var charstring id := "Asterisk_Tests_AMI_EMU";
+ vc_AMI := AMI_Adapter_CT.create(id) alive;
+ connect(self:AMI_CLIENT, vc_AMI:CLIENT);
+
+ var AMI_Adapter_Parameters ami_pars := {
+ remote_host := mp_ami_remote_host,
+ remote_port := mp_ami_remote_port,
+ local_host := mp_ami_local_host,
+ local_port := mp_ami_local_port,
+ welcome_str := c_default_AMI_Adapter_pars.welcome_str
+ };
+ vc_AMI.start(f_AMI_Adapter_main(ami_pars));
+
+ f_ami_action_login(AMI_CLIENT, mp_ami_user, mp_ami_secret);
+
+ timer tReady;
+ tReady.start(mp_ami_ev_fullybooted_timeout);
+ alt {
+ [] AMI_CLIENT.receive(tr_AMI_Event_FullyBooted);
+ [] as_ami_rx_ignore(AMI_CLIENT);
+ [] tReady.timeout {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("AMI FullyBooted timeout: "));
+ }
+ }
+}
+
+/* Local SIP UAs */
+private function f_init_sip_local() runs on test_CT {
+ var charstring id := "Asterisk_Tests_LOCAL_SIP_EMU";
+ f_init_sip(vc_SIP, id);
+}
+
+/* IMS Server connection */
+private function f_init_sip_ims() runs on test_CT {
+ var charstring id := "Asterisk_Tests_IMS_SIP_EMU";
+ f_init_sip(vc_IMS, id);
+}
+
+function f_init() runs on test_CT {
+ var charstring id;
+
+ f_init_ami();
+ f_init_sip_local();
+ f_init_sip_ims();
+ log("end of f_init");
+}
+
+function f_shutdown() runs on test_CT {
+ /* Tear down AMI Adapter to avoid it keep receiving data from Asterisk
+ * and sending it to us after we stopped, causing error (Broken Pipe): */
+ vc_AMI.stop;
+ vc_AMI.done;
+ log("end of ", testcasename());
+ setverdict(pass);
+}
+
+function f_start_handler(void_fn fn, SIPConnHdlrPars pars)
+runs on test_CT return SIPConnHdlr {
+ var SIPConnHdlr vc_conn;
+ var charstring id := testcasename() & "-ConnHdlr-" & pars.user;
+
+ vc_conn := SIPConnHdlr.create(id) alive;
+
+ connect(vc_conn:SIP, vc_SIP:CLIENT);
+ connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);
+
+ connect(vc_conn:COORD, self:COORD);
+
+ vc_conn.start(f_handler_init(fn, id, pars));
+ return vc_conn;
+}
+
+function f_start_handler_IMS(ims_void_fn fn, IMS_ConnHdlrPars pars)
+runs on test_CT return IMS_ConnHdlr {
+ var IMS_ConnHdlr vc_conn;
+ var charstring id := testcasename() & "-IMS_ConnHdlr-" & pars.subscr.imsi;
+
+ vc_conn := IMS_ConnHdlr.create(id) alive;
+
+ connect(vc_conn:SIP, vc_IMS:CLIENT);
+ connect(vc_conn:SIP_PROC, vc_IMS:CLIENT_PROC);
+
+ connect(vc_conn:COORD, self:IMS_COORD);
+
+ vc_conn.start(f_ims_handler_init(fn, id, pars));
+ return vc_conn;
+}
+
+/* Test SIP registration of local clients */
+private function f_TC_internal_registration(charstring id) runs on SIPConnHdlr {
+
+ f_SIP_register();
+ /* Trigger unregistration: */
+ f_sleep(1.0);
+ f_SIP_unregister();
+ setverdict(pass);
+}
+testcase TC_internal_registration() runs on test_CT {
+ var SIPConnHdlrPars pars;
+ var SIPConnHdlr vc_conn;
+ f_init();
+ pars := f_init_ConnHdlrPars();
+ vc_conn := f_start_handler(refers(f_TC_internal_registration), pars);
+ vc_conn.done;
+ f_shutdown();
+}
+
+/* Successful SIP MO-MT Call between local clients: */
+private function f_TC_internal_register_establish_call_mo() runs on SIPConnHdlr {
+ f_SIP_register();
+ COORD.send(COORD_CMD_REGISTERED);
+
+ COORD.receive(COORD_CMD_START);
+ f_SIP_mo_call_setup(exp_update_to_direct_rtp := g_pars.cp.exp_update_to_direct_rtp);
+ COORD.send(COORD_CMD_CALL_ESTABLISHED);
+}
+private function f_TC_internal_hangup_call_mo_unregister() runs on SIPConnHdlr {
+ COORD.receive(COORD_CMD_HANGUP);
+ f_SIP_do_call_hangup();
+
+ COORD.receive(COORD_CMD_UNREGISTER);
+ f_SIP_unregister();
+}
+private function f_TC_internal_call_mo(charstring id) runs on SIPConnHdlr {
+ f_TC_internal_register_establish_call_mo();
+ f_TC_internal_hangup_call_mo_unregister();
+ setverdict(pass);
+}
+private function f_TC_internal_call_mo_with_holdresume(charstring id) runs on SIPConnHdlr {
+ f_TC_internal_register_establish_call_mo();
+ f_sleep(1.0);
+ f_SIP_do_call_hold();
+ f_sleep(1.0);
+ f_SIP_do_call_resume();
+ f_TC_internal_hangup_call_mo_unregister();
+ setverdict(pass);
+}
+private function f_TC_internal_call_mo_with_holdswitchresume(charstring id) runs on SIPConnHdlr {
+ var CallPars cp0;
+ f_TC_internal_register_establish_call_mo();
+ f_sleep(1.0);
+ f_SIP_do_call_hold();
+
+ /* Backup state of 1st call params: */
+ cp0 := g_pars.cp;
+ g_pars.cp.called := valueof(ts_SipAddr(ts_HostPort(g_pars.remote_sip_host),
+ ts_UserInfo("123456")));
+ COORD.receive(COORD_CMD_START);
+ f_SIP_mo_call_setup(exp_update_to_direct_rtp := false);
+ log ("SIP: 2nd call ongoing");
+ COORD.send(COORD_CMD_CALL_ESTABLISHED);
+ COORD.receive(COORD_CMD_HANGUP);
+ f_SIP_do_call_hangup();
+
+ /* Restore 1s call metadata before hanging it up here: */
+ g_pars.cp := cp0;
+ f_SIP_do_call_resume();
+ f_TC_internal_hangup_call_mo_unregister();
+ setverdict(pass);
+}
+private function f_TC_internal_call_mo_rejected(charstring id) runs on SIPConnHdlr {
+ f_SIP_register();
+ COORD.send(COORD_CMD_REGISTERED);
+
+ COORD.receive(COORD_CMD_START);
+ f_SIP_mo_call_setup(503, "Service Unavailable");
+ COORD.send(COORD_CMD_CALL_FINISHED);
+
+ COORD.receive(COORD_CMD_UNREGISTER);
+ f_SIP_unregister();
+ setverdict(pass);
+}
+
+private function f_TC_internal_call_mt(charstring id) runs on SIPConnHdlr {
+
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.cp.called.addr)));
+
+ f_SIP_register();
+ COORD.send(COORD_CMD_REGISTERED);
+
+ if (g_pars.cp.mt.exp_cancel) {
+ as_SIP_mt_call_cancelled();
+ COORD.send(COORD_CMD_CALL_CANCELLED);
+ COORD.receive(COORD_CMD_UNREGISTER);
+ f_SIP_unregister();
+ setverdict(pass);
+ return;
+ }
+
+ as_SIP_mt_call_accept(exp_update_to_direct_rtp := g_pars.cp.exp_update_to_direct_rtp);
+ COORD.send(COORD_CMD_CALL_ESTABLISHED);
+
+ if (g_pars.cp.exp_update_to_direct_rtp) {
+ /* Once MO hangs up, Asterisk updates us to point RTP to it: */
+ as_SIP_exp_mt_call_update(g_pars.cp.sip_seq_nr + 1);
+ }
+ as_SIP_exp_call_hangup(g_pars.cp.sip_seq_nr + 1);
+
+ COORD.send(COORD_CMD_CALL_FINISHED);
+ COORD.receive(COORD_CMD_UNREGISTER);
+ f_SIP_unregister();
+
+ setverdict(pass);
+}
+testcase TC_internal_call_momt() runs on test_CT {
+ var SIPConnHdlrPars pars[2];
+ var SIPConnHdlr vc_conn[2];
+
+ f_init();
+
+ pars[0] := f_init_ConnHdlrPars(idx := 1);
+ pars[1] := f_init_ConnHdlrPars(idx := 2);
+
+ pars[0].cp.calling := pars[0].registrar_sip_record;
+ pars[0].cp.called := pars[1].registrar_sip_record;
+
+ pars[1].cp.calling := pars[0].registrar_sip_record;
+ pars[1].cp.called := pars[1].local_sip_record;
+
+ vc_conn[0] := f_start_handler(refers(f_TC_internal_call_mo), pars[0]);
+ vc_conn[1] := f_start_handler(refers(f_TC_internal_call_mt), pars[1]);
+
+ interleave {
+ [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[0];
+ [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[1];
+ }
+
+ COORD.send(COORD_CMD_START) to vc_conn[0];
+
+ interleave {
+ [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[0];
+ [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[1];
+ }
+
+ /* Call on-going */
+ f_sleep(1.0);
+
+ COORD.send(COORD_CMD_HANGUP) to vc_conn[0];
+ COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn[1];
+
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn[0];
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn[1];
+
+ vc_conn[0].done;
+ vc_conn[1].done;
+ f_shutdown();
+}
+
+/* One of the users calls (INVITE) shared extension, which makes all other user
+ * equipments ring (INVITE). The first one to pick up the call (OK 200) gets the
+ * call established (ACK), others get a CANCEL event. */
+private function TC_internal_call_all_Nregistered(integer num_conns := 2) runs on test_CT {
+ var SIPConnHdlrList vc_conn_list := {};
+ const integer vc_conn_mo_idx := 0; /* Index of MO leg in vc_conn_list */
+ const integer vc_conn_mt_idx := 1; /* Index of MT leg in vc_conn_list, peer picking up first the call */
+ var SipAddr broadcast_sip_record;
+ var SIPConnHdlrPars pars_mo;
+
+ f_init();
+
+ broadcast_sip_record := valueof(ts_SipAddr(ts_HostPort(mp_local_sip_host),
+ ts_UserInfo(broadcast_sip_extension)));
+
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ var SIPConnHdlrPars pars;
+ var SIPConnHdlr vc_conn;
+ pars := f_init_ConnHdlrPars(idx := i + 1);
+ if (i == vc_conn_mo_idx) { /* MO */
+ pars.cp.calling := pars.registrar_sip_record;
+ pars.cp.called := broadcast_sip_record;
+ vc_conn := f_start_handler(refers(f_TC_internal_call_mo), pars);
+ pars_mo := pars;
+ } else { /* MT */
+ pars.cp.calling := pars_mo.registrar_sip_record;
+ pars.cp.called := pars.local_sip_record;
+ pars.cp.mt.wait_coord_cmd_pickup := true;
+ if (i != vc_conn_mt_idx) {
+ /* Only first MT picking up (OK 200 INVITE) will be ACKed, others CANCELed: */
+ pars.cp.mt.exp_cancel := true;
+ }
+ vc_conn := f_start_handler(refers(f_TC_internal_call_mt), pars);
+ }
+ vc_conn_list := vc_conn_list & { vc_conn };
+ }
+
+ /* Wait all users are registered: */
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ /* Note: "from vc_conn_list[i]" can't be used since they may arrive from components in any order: */
+ COORD.receive(COORD_CMD_REGISTERED);
+ }
+
+ /* Ask MO user to start the call: */
+ COORD.send(COORD_CMD_START) to vc_conn_list[vc_conn_mo_idx];
+
+ /* Make sure the desired MT is the one picking up first the call: */
+ COORD.send(COORD_CMD_PICKUP) to vc_conn_list[vc_conn_mt_idx];
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ if (i != vc_conn_mo_idx and i != vc_conn_mt_idx) {
+ COORD.send(COORD_CMD_PICKUP) to vc_conn_list[i];
+ }
+ }
+
+ /* Pick up from other phone calls and expect CANCEL: */
+ var boolean wait_mo := true, wait_mt := true;
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ alt {
+ [wait_mo] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_list[vc_conn_mo_idx] { wait_mo := false };
+ [wait_mt] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_list[vc_conn_mt_idx] { wait_mt := false };
+ [] COORD.receive(COORD_CMD_CALL_CANCELLED);
+ }
+ }
+
+ /* Call on-going */
+ f_sleep(1.0);
+
+ COORD.send(COORD_CMD_HANGUP) to vc_conn_list[vc_conn_mo_idx];
+
+ COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_list[vc_conn_mt_idx];
+
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_list[i];
+ }
+
+ for (var integer i := 0; i < num_conns; i := i + 1) {
+ vc_conn_list[i].done;
+ }
+ f_shutdown();
+}
+testcase TC_internal_call_all_2registered() runs on test_CT {
+ TC_internal_call_all_Nregistered(2);
+}
+testcase TC_internal_call_all_3registered() runs on test_CT {
+ TC_internal_call_all_Nregistered(3);
+}
+testcase TC_internal_call_all_4registered() runs on test_CT {
+ TC_internal_call_all_Nregistered(4);
+}
+
+testcase TC_selftest() runs on test_CT {
+ f_ami_selftest();
+ f_sip_digest_selftest();
+ setverdict(pass);
+}
+
+private function f_AMI_IMS_start_register(IMS_ConnHdlrPars pars) runs on test_CT
+{
+ /* Give some time for IMS_ConnHdlr to register SIP expect. This could be done through IMS_COORD. */
+ f_sleep(1.0);
+ /* Clear events: */
+ AMI_CLIENT.clear;
+
+ /* Announce network information, this should usually happen when UE
+ * becomes attached to network and before IMS APN is set up: */
+ f_ami_action_PJSIPAccessNetworkInfo(AMI_CLIENT, mp_volte_ims_outbound_registration,
+ f_ami_gen_PJSIPAccessNetworkInfo_Info_EUTRAN(pars.subscr.uli_str));
+
+ /* Trigger registration: */
+ f_ami_action_PJSIPRegister(AMI_CLIENT, mp_volte_ims_outbound_registration);
+}
+
+private altstep as_AMI_IMS_register_Auth(IMS_ConnHdlrPars pars,
+ boolean resync := false,
+ boolean exp_ami_ev_registered := true) runs on test_CT
+{
+ var charstring rand_str := oct2str(pars.subscr.auth.rand);
+ var charstring autn_str := oct2str(pars.subscr.auth.autn);
+ [not resync] AMI_CLIENT.receive(tr_AMI_Event_AuthRequest(mp_volte_ims_outbound_registration,
+ rand := pattern @nocase rand_str,
+ autn := pattern @nocase autn_str)) {
+ f_ami_action_AuthResponse_RES(AMI_CLIENT,
+ mp_volte_ims_outbound_registration,
+ f_str_tolower(oct2str(pars.subscr.auth.res)),
+ f_str_tolower(oct2str(pars.subscr.auth.ck)),
+ f_str_tolower(oct2str(pars.subscr.auth.ik)));
+
+ if (exp_ami_ev_registered) {
+ var AMI_Msg ami_msg_rx;
+ var template (present) AMI_Msg ami_msg_exp :=
+ tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record),
+ "sip:" & mp_ims_domain,
+ "Registered");
+ alt {
+ [] AMI_CLIENT.receive(ami_msg_exp);
+ [] AMI_CLIENT.receive(AMI_Msg:?) -> value ami_msg_rx {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Unexpected AMI msg: ", ami_msg_rx, " vs exp: ", ami_msg_exp));
+ }
+ }
+ }
+ }
+ [resync] AMI_CLIENT.receive(tr_AMI_Event_AuthRequest(mp_volte_ims_outbound_registration,
+ rand := pattern @nocase rand_str,
+ autn := pattern @nocase autn_str)) {
+ f_ami_action_AuthResponse_AUTS(AMI_CLIENT,
+ mp_volte_ims_outbound_registration,
+ f_str_tolower(oct2str(pars.subscr.auth.auts)));
+ }
+}
+
+private function f_AMI_IMS_register(IMS_ConnHdlrPars pars, boolean resync := false) runs on test_CT
+{
+ f_AMI_IMS_start_register(pars);
+ as_AMI_IMS_register_Auth(pars, resync := resync);
+}
+
+private function f_AMI_IMS_unregister(IMS_ConnHdlrPars pars) runs on test_CT
+{
+ f_ami_action_PJSIPUnregister(AMI_CLIENT, mp_volte_ims_outbound_registration, fail_others := false);
+ var template (present) AMI_Msg msg_expect :=
+ tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record),
+ "sip:" & mp_ims_domain,
+ "Unregistered");
+ f_ami_wait_rx_msg(AMI_CLIENT, msg_expect, fail_others := false);
+}
+
+/* Test IMS registration of VoLTE UE */
+private function f_TC_ims_registration(charstring id) runs on IMS_ConnHdlr {
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+ as_IMS_register();
+ as_IMS_unregister();
+ setverdict(pass);
+}
+testcase TC_ims_registration() runs on test_CT {
+ var IMS_ConnHdlrPars pars;
+ var IMS_ConnHdlr vc_conn;
+ f_init();
+ pars := f_init_IMS_ConnHdlrPars();
+ vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration), pars);
+
+ f_AMI_IMS_register(pars);
+
+ /* Stay registered for one second */
+ f_sleep(1.0);
+
+ /* Trigger unregistration: */
+ f_AMI_IMS_unregister(pars);
+
+ vc_conn.done;
+ f_shutdown();
+}
+
+/* Test IMS registration of VoLTE UE. ISIM Resync case. */
+private function f_TC_ims_registration_resync(charstring id) runs on IMS_ConnHdlr {
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+ as_IMS_register(exp_auth_resync := true);
+ as_IMS_unregister();
+ setverdict(pass);
+}
+testcase TC_ims_registration_resync() runs on test_CT {
+ var IMS_ConnHdlrPars pars;
+ var IMS_ConnHdlr vc_conn;
+ f_init();
+ pars := f_init_IMS_ConnHdlrPars();
+ vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration_resync), pars);
+
+ /* Emulate first Auth sync failure: */
+ f_AMI_IMS_register(pars, resync := true);
+ /* Second auth goes well: */
+ as_AMI_IMS_register_Auth(pars, resync := false);
+ /* Stay registered for one second */
+ f_sleep(1.0);
+
+ /* Trigger unregistration: */
+ f_AMI_IMS_unregister(pars);
+
+ vc_conn.done;
+ f_shutdown();
+}
+
+private function f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_register_early_return early_ret, boolean exp_auth_resync := false) runs on IMS_ConnHdlr {
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+
+ as_IMS_register(exp_auth_resync := exp_auth_resync, early_ret := early_ret);
+
+ timer Tout;
+ /* 1.0: Give some margin. */
+ Tout.start(int2float(mp_volte_ims_outbound_register_timeout) + 1.0);
+ /* Make sure no new REGISTER is attempted unless directed by AMI: */
+ alt {
+ [] as_SIP_fail_req("nothing");
+ [] as_SIP_fail_resp("nothing");
+ [] Tout.timeout { /* Done */ }
+ }
+ setverdict(pass);
+}
+/* Test initial REGISTER against IMS core timing out. */
+private function f_TC_ims_registration_timeout_initial_100Trying(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Initial_100Trying);
+}
+private function f_TC_ims_registration_timeout_initial_401Unauthorized(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Initial_401Unauthorized);
+}
+private function f_TC_ims_registration_timeout_resync_401Unauthorized(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Resync_401Unauthorized, exp_auth_resync := true);
+}
+private function f_TC_ims_registration_timeout_protected_100Trying(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Protected_100Trying);
+}
+private function f_TC_ims_registration_timeout_protected_200OK(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Protected_200OK);
+}
+private function f_TC_ims_registration_timeout_initial(ims_void_fn fn,
+ boolean answer_register := false,
+ boolean resync := false) runs on test_CT {
+ var IMS_ConnHdlrPars pars;
+ var IMS_ConnHdlr vc_conn;
+ f_init();
+ pars := f_init_IMS_ConnHdlrPars();
+ vc_conn := f_start_handler_IMS(fn, pars);
+
+ f_AMI_IMS_start_register(pars);
+
+ if (answer_register) {
+ as_AMI_IMS_register_Auth(pars, resync := resync, exp_ami_ev_registered := false);
+ }
+
+ AMI_CLIENT.receive(tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record),
+ "sip:" & mp_ims_domain,
+ "Rejected"));
+
+ vc_conn.done;
+ f_shutdown();
+}
+testcase TC_ims_registration_timeout_initial_100Trying() runs on test_CT {
+ f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_initial_100Trying));
+}
+testcase TC_ims_registration_timeout_initial_401Unauthorized() runs on test_CT {
+ f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_initial_401Unauthorized));
+}
+testcase TC_ims_registration_timeout_resync_401Unauthorized() runs on test_CT {
+ f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_resync_401Unauthorized),
+ true, true);
+}
+testcase TC_ims_registration_timeout_protected_100Trying() runs on test_CT {
+ f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_protected_100Trying),
+ true, false);
+}
+testcase TC_ims_registration_timeout_protected_200OK() runs on test_CT {
+ f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_protected_200OK),
+ true, false);
+}
+
+/* Test IMS re-registration based on Expires (TS 24.229 5.1.1.4.1, RFC 3261 10.3)*/
+private function f_TC_ims_registration_423_interval_too_brief(charstring id) runs on IMS_ConnHdlr {
+ var template (value) To to_addr;
+
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+
+ var template (present) PDU_SIP_Request exp_req :=
+ tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
+ ?,
+ tr_From(),
+ tr_To(),
+ tr_Via_from(?),
+ expires := tr_Expires(int2str(g_pars.subscr.registrar_expires)));
+ SIP.receive(exp_req) -> value g_rx_sip_req;
+
+ to_addr := g_rx_sip_req.msgHeader.toField;
+ to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
+
+ /* Double it and expect UAC to use it. */
+ g_pars.subscr.registrar_expires := g_pars.subscr.registrar_expires * 2;
+
+ /* Tx 423 Interval Too Brief */
+ var template (value) PDU_SIP_Response tx_resp;
+ tx_resp := ts_SIP_Response_423_Interval_Too_Brief(
+ g_rx_sip_req.msgHeader.callId.callid,
+ g_rx_sip_req.msgHeader.fromField,
+ to_addr,
+ g_rx_sip_req.msgHeader.via,
+ g_rx_sip_req.msgHeader.cSeq.seqNumber,
+ minExpires := ts_MinExpires(int2str(g_pars.subscr.registrar_expires)),
+ server := g_pars.server_name,
+ userAgent := omit);
+ SIP.send(tx_resp);
+
+ g_pars.subscr.exp_uac_expires := g_pars.subscr.registrar_expires;
+ as_IMS_register();
+
+ as_IMS_unregister();
+ setverdict(pass);
+}
+testcase TC_ims_registration_423_interval_too_brief() runs on test_CT {
+ var IMS_ConnHdlrPars pars;
+ var IMS_ConnHdlr vc_conn;
+ f_init();
+ pars := f_init_IMS_ConnHdlrPars();
+ vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration_423_interval_too_brief), pars);
+
+ f_AMI_IMS_register(pars);
+
+ /* Stay registered for one second */
+ f_sleep(1.0);
+
+ /* Trigger unregistration: */
+ f_AMI_IMS_unregister(pars);
+
+ vc_conn.done;
+ f_shutdown();
+}
+
+/* Test IMS re-registration based on Expires (TS 24.229 5.1.1.4.1, RFC 3261 10.3)*/
+private function f_TC_ims_reregistration(charstring id) runs on IMS_ConnHdlr {
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+ as_IMS_register();
+
+ timer Trereg;
+ Trereg.start(int2float(g_pars.subscr.registrar_expires) - 5.0);
+ g_pars.subscr.registrar_expires := c_def_expires;
+ alt {
+ [] as_IMS_2nd_register();
+ [] Trereg.timeout {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(g_name & ": Timeout waiting for re-registration"));
+ }
+ }
+ Trereg.stop;
+
+ as_IMS_unregister();
+ setverdict(pass);
+}
+testcase TC_ims_reregistration() runs on test_CT {
+ var IMS_ConnHdlrPars pars;
+ var IMS_ConnHdlr vc_conn;
+ f_init();
+ pars := f_init_IMS_ConnHdlrPars();
+ pars.subscr.registrar_expires := 30;
+ vc_conn := f_start_handler_IMS(refers(f_TC_ims_reregistration), pars);
+
+ f_AMI_IMS_register(pars);
+
+ /* Wait for Asterisk to re-register and IMS_ConnHdlr to handle it... */
+ f_sleep(int2float(pars.subscr.registrar_expires));
+
+ /* Trigger unregistration: */
+ f_AMI_IMS_unregister(pars);
+
+ vc_conn.done;
+ f_shutdown();
+}
+
+private function f_ims_call_mo_configure(inout SIPConnHdlrPars sip_pars, inout IMS_ConnHdlrPars ims_pars,
+ charstring tgt_ext_msisdn := "90829") runs on test_CT {
+ sip_pars.cp.exp_update_to_direct_rtp := false;
+ sip_pars.cp.calling := sip_pars.registrar_sip_record;
+ sip_pars.cp.called := valueof(ts_SipAddr(ts_HostPort(sip_pars.remote_sip_host),
+ ts_UserInfo(tgt_ext_msisdn)));
+ ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(ims_pars.subscr.msisdn)));
+ ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(tgt_ext_msisdn)));
+}
+
+private function f_ims_call_mo_establish(inout SIPConnHdlr vc_conn_sip, inout IMS_ConnHdlr vc_conn_ims) runs on test_CT {
+ var AMI_Msg ami_msg;
+
+ COORD.send(COORD_CMD_START) to vc_conn_sip;
+
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_TRYING) from vc_conn_ims;
+ ami_msg := f_ami_wait_rx_msg(AMI_CLIENT,
+ tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration),
+ fail_others := false);
+ g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL));
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Up", fail_others := false);
+
+ COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip;
+ IMS_COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_ims;
+}
+
+private function f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call() 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)));
+ }
+ as_IMS_register();
+ setverdict(pass);
+ as_IMS_mo_call_accept();
+ setverdict(pass);
+ COORD.send(COORD_CMD_CALL_ESTABLISHED);
+}
+
+private function f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister() runs on IMS_ConnHdlr {
+ as_IMS_exp_call_hangup(g_pars.subscr.cp.sip_seq_nr + 1);
+ COORD.send(IMS_COORD_CMD_CALL_FINISHED);
+ setverdict(pass);
+ as_IMS_unregister();
+}
+private function f_TC_ims_call_mo_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call();
+ if (g_pars.subscr.cp.mo.support_timer_session_expires > 0) {
+ timer Trefresh;
+ Trefresh.start(int2float(g_pars.subscr.cp.mo.support_timer_session_expires));
+ alt {
+ [] as_IMS_exp_call_refresh();
+ [] Trefresh.timeout {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(g_name & ": Timeout waiting for Session Refresh"));
+ }
+ }
+ Trefresh.stop;
+ /* Sleep to make sure response arrives to the peer */
+ f_sleep(1.0);
+ COORD.send(IMS_COORD_CMD_CALL_SESSION_REFRESH);
+ }
+ f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister();
+}
+
+private function f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume(charstring id) runs on IMS_ConnHdlr {
+ f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call();
+ as_IMS_exp_call_hold();
+ f_sleep(1.0);
+ as_IMS_exp_call_resume();
+ f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister();
+}
+private function f_TC_ims_call_mo_IMS_ConnHdlr_with_holdswitchresume(charstring id) runs on IMS_ConnHdlr {
+ var IMS_ConnHdlrSubscrPars subscr0;
+ var Addr_Union called_addr;
+
+ f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call();
+
+ called_addr := g_pars.subscr.cp.called.addr;
+ called_addr.nameAddr.addrSpec.userInfo := valueof(ts_UserInfo("123456"));
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(called_addr)));
+
+ as_IMS_exp_call_hold();
+
+ /* Backup state of 1st call subscriber: */
+ subscr0 := g_pars.subscr;
+ g_pars.subscr.cp.called.addr := called_addr;
+ as_IMS_mo_call_accept();
+ COORD.send(COORD_CMD_CALL_ESTABLISHED);
+ log ("IMS: 2nd call ongoing");
+
+ as_IMS_exp_call_hangup(g_pars.subscr.cp.sip_seq_nr + 1);
+ COORD.send(IMS_COORD_CMD_CALL_FINISHED);
+ /* Restore 1s call metadata before hanging it up here: */
+ g_pars.subscr := subscr0;
+
+ as_IMS_exp_call_resume();
+ f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister();
+}
+private function f_TC_ims_call_mo_IMS_ConnHdlr_2nd_mt_rejected(charstring id) runs on IMS_ConnHdlr {
+ var IMS_ConnHdlrSubscrPars subscr0;
+ var default d_trying, d_ringing, d_sessprog;
+ var Via via;
+
+ /* Establish 1st (MO) call */
+ f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call();
+
+ /* Backup state of 1st call subscriber: */
+ subscr0 := g_pars.subscr;
+ /* Initiate second (MT) call: */
+ f_IMS_mt_call_rejected(486, "Busy Here");
+ /* Leave some time for last ACK from IMS to arrive to Asterisk */
+ f_sleep(1.0);
+ /* Restore 1s call metadata before hanging it up here: */
+ g_pars.subscr := subscr0;
+ COORD.send(IMS_COORD_CMD_CALL_REJECTED);
+
+ f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister();
+}
+
+type function call_established_fn(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT;
+private function call_established_fn_sleep1(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT {
+ f_sleep(1.0);
+}
+private function call_established_fn_wait_ims_coord_cmd_call_rejected(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT {
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED);
+}
+
+private function f_TC_ims_call_mo(boolean close_tcp_after_registration := false,
+ boolean use_precondition_ext := true,
+ boolean use_session_timer := false,
+ call_established_fn call_established_cb := refers(call_established_fn_sleep1),
+ void_fn sip_fn := refers(f_TC_internal_call_mo),
+ ims_void_fn ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr)) runs on test_CT {
+ var SIPConnHdlrPars sip_pars;
+ var IMS_ConnHdlrPars ims_pars;
+ var SIPConnHdlr vc_conn_sip;
+ var IMS_ConnHdlr vc_conn_ims;
+
+ f_init();
+
+ sip_pars := f_init_ConnHdlrPars(idx := 1);
+ ims_pars := f_init_IMS_ConnHdlrPars();
+ ims_pars.subscr.close_tcp_after_registration := close_tcp_after_registration;
+
+ f_ims_call_mo_configure(sip_pars, ims_pars);
+ ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext;
+ ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext;
+ ims_pars.subscr.cp.mo.tx_coord_cmd_invite_trying := true;
+ ims_pars.subscr.cp.mo.support_timer_enable := use_session_timer;
+ if (use_session_timer) {
+ sip_pars.t_guard := 120.0;
+ ims_pars.t_guard := 120.0;
+ ims_pars.subscr.cp.mo.support_timer_exp_min_se := 90;
+ ims_pars.subscr.cp.mo.support_timer_session_expires := 90;
+
+ }
+
+ vc_conn_ims := f_start_handler_IMS(ims_fn, ims_pars);
+ vc_conn_sip := f_start_handler(sip_fn, sip_pars);
+
+ COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip;
+
+ f_AMI_IMS_register(ims_pars);
+
+ f_ims_call_mo_establish(vc_conn_sip, vc_conn_ims);
+
+ /* Call on-going */
+ if (use_session_timer) {
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_SESSION_REFRESH);
+ }
+ call_established_cb.apply(sip_pars, ims_pars);
+
+ COORD.send(COORD_CMD_HANGUP) to vc_conn_sip;
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED) from vc_conn_ims;
+
+ /* Notify network released dedicated bearer: */
+ AMI_CLIENT.clear;
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Down", fail_others := false);
+
+ /* Trigger unregistration: */
+ f_sleep(1.0);
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip;
+ AMI_CLIENT.clear;
+ f_AMI_IMS_unregister(ims_pars);
+
+ vc_conn_sip.done;
+ vc_conn_ims.done;
+ f_shutdown();
+}
+testcase TC_ims_call_mo() runs on test_CT {
+ f_TC_ims_call_mo();
+}
+testcase TC_ims_call_mo_session_timer() runs on test_CT {
+ f_TC_ims_call_mo(use_session_timer := true);
+}
+testcase TC_ims_call_mo_noprecondition() runs on test_CT {
+ f_TC_ims_call_mo(use_precondition_ext := false);
+}
+testcase TC_ims_call_mo_holdresume_mo() runs on test_CT {
+ f_TC_ims_call_mo(sip_fn := refers(f_TC_internal_call_mo_with_holdresume),
+ ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume));
+}
+/* TC_ims_call_mo, but IMS server closes the TCP conn used during 2nd register after ACKing it.
+ * Asterisk should consider it is still registered even if that TCP conn was closed,
+ * and MO call should work the same.
+ */
+testcase TC_ims_call_mo_after_tcp_conn_closed() runs on test_CT {
+ f_TC_ims_call_mo(close_tcp_after_registration := true);
+}
+
+/* Test a 2nd call initiated from a local SIP UA towards IMS after having HOLD the 1st one. */
+private function call_established_fn_holdswitchresume(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT {
+ var AMI_Msg ami_msg;
+ var charstring old_asterisk_chan_name := g_asterisk_chan_name; /* backup */
+ COORD.send(COORD_CMD_START);
+
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_TRYING);
+ ami_msg := f_ami_wait_rx_msg(AMI_CLIENT,
+ tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration),
+ fail_others := false);
+ g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL));
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Up", fail_others := false);
+
+ COORD.receive(COORD_CMD_CALL_ESTABLISHED);
+ IMS_COORD.receive(COORD_CMD_CALL_ESTABLISHED);
+ /* 2nd call ongoing */
+ log ("test_CT: 2nd call ongoing");
+ f_sleep(1.0);
+ /* Tear down 2nd call: */
+ COORD.send(COORD_CMD_HANGUP);
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED);
+
+ /* Notify network released 2nd dedicated bearer: */
+ AMI_CLIENT.clear;
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Down", fail_others := false);
+
+ g_asterisk_chan_name := old_asterisk_chan_name; /* restore */
+}
+testcase TC_ims_call_mo_holdswitchresume_mo() runs on test_CT {
+ f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_holdswitchresume),
+ sip_fn := refers(f_TC_internal_call_mo_with_holdswitchresume),
+ ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_with_holdswitchresume));
+}
+
+/* Test a 2nd call initiated from a local SIP UA towards IMS is rejected with
+ * 503 Service Unavailable if IMS endpoint is already busy in a call. */
+private function call_established_fn_2ndrejected(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT {
+ var SIPConnHdlrPars sip_pars2;
+ var SIPConnHdlr vc_conn_sip2;
+ sip_pars2 := f_init_ConnHdlrPars(idx := 2);
+ f_ims_call_mo_configure(sip_pars2, ims_pars, tgt_ext_msisdn := "88888888");
+ vc_conn_sip2 := f_start_handler(refers(f_TC_internal_call_mo_rejected), sip_pars2);
+
+ COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip2;
+ COORD.send(COORD_CMD_START) to vc_conn_sip2;
+ COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip2;
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip2;
+ vc_conn_sip2.done;
+}
+testcase TC_ims_call_mo_2nd_mo_rejected() runs on test_CT {
+ f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_2ndrejected));
+}
+
+/* Test a 2nd call initiated from IMS towards Astierisk is rejected with
+ * 486 Busy Here if IMS endpoint is already busy in a call. */
+testcase TC_ims_call_mo_2nd_mt_rejected() runs on test_CT {
+ f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_wait_ims_coord_cmd_call_rejected),
+ ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_2nd_mt_rejected));
+}
+
+/* Test MT call initiated by IMS */
+private function f_TC_ims_call_mt_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)));
+ as_IMS_register();
+ COORD.send(IMS_COORD_CMD_REGISTERED);
+ setverdict(pass);
+
+ COORD.receive(IMS_COORD_CMD_START);
+ f_IMS_mt_call_setup();
+ setverdict(pass);
+ COORD.send(IMS_COORD_CMD_CALL_ESTABLISHED);
+
+ COORD.receive(IMS_COORD_CMD_HANGUP);
+ f_IMS_do_call_hangup();
+ setverdict(pass);
+ COORD.send(IMS_COORD_CMD_CALL_FINISHED);
+
+ as_IMS_unregister();
+}
+private function f_TC_ims_call_mt(boolean use_precondition_ext) runs on test_CT {
+ var SIPConnHdlrPars sip_pars;
+ var IMS_ConnHdlrPars ims_pars;
+ var SIPConnHdlr vc_conn_sip;
+ var IMS_ConnHdlr vc_conn_ims;
+ var AMI_Msg ami_msg;
+ const charstring c_ext_msisdn := "90829";
+
+ f_init();
+
+ sip_pars := f_init_ConnHdlrPars(idx := 1);
+ ims_pars := f_init_IMS_ConnHdlrPars();
+
+ sip_pars.cp.exp_update_to_direct_rtp := false;
+ sip_pars.cp.calling := valueof(ts_SipAddr(ts_HostPort(sip_pars.remote_sip_host),
+ ts_UserInfo(c_ext_msisdn)));
+ sip_pars.cp.called := sip_pars.local_sip_record;
+ ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(c_ext_msisdn)));
+ ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(ims_pars.subscr.msisdn)));
+ ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext;
+ ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext;
+ ims_pars.subscr.cp.mt.tx_coord_cmd_session_progress := use_precondition_ext;
+
+ vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr), ims_pars);
+ vc_conn_sip := f_start_handler(refers(f_TC_internal_call_mt), sip_pars);
+
+ COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip;
+
+ f_AMI_IMS_register(ims_pars);
+ IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims;
+
+ IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims;
+
+ ami_msg := f_ami_wait_rx_msg(AMI_CLIENT,
+ tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration),
+ fail_others := false);
+ g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL));
+ if (ims_pars.subscr.cp.mt.tx_coord_cmd_session_progress) {
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_SESSION_PROGRESS) from vc_conn_ims;
+ }
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Up", fail_others := false);
+
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_ESTABLISHED) from vc_conn_ims;
+ COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip;
+
+ /* Call on-going */
+ f_sleep(1.0);
+
+ IMS_COORD.send(IMS_COORD_CMD_HANGUP) to vc_conn_ims;
+
+ /* Notify network released dedicated bearer: */
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED) from vc_conn_ims;
+ AMI_CLIENT.clear;
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Down", fail_others := false);
+ COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip;
+
+ /* Trigger unregistration: */
+ f_sleep(1.0);
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip;
+ AMI_CLIENT.clear;
+ f_AMI_IMS_unregister(ims_pars);
+
+ vc_conn_sip.done;
+ vc_conn_ims.done;
+ f_shutdown();
+}
+testcase TC_ims_call_mt() runs on test_CT {
+ f_TC_ims_call_mt(true);
+}
+testcase TC_ims_call_mt_noprecondition() runs on test_CT {
+ f_TC_ims_call_mt(false);
+}
+
+/* Validate incoming IMS MT call is rejected if no UAs are registered, hence not available. */
+private function f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline(charstring id) runs on IMS_ConnHdlr {
+ f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
+ as_IMS_register();
+ COORD.send(IMS_COORD_CMD_REGISTERED);
+ setverdict(pass);
+
+ COORD.receive(IMS_COORD_CMD_START);
+ f_IMS_mt_call_rejected(603, "Decline");
+ setverdict(pass);
+ COORD.send(IMS_COORD_CMD_CALL_REJECTED);
+
+ as_IMS_unregister();
+}
+testcase TC_ims_call_mt_no_local_uas_registered() runs on test_CT {
+ var IMS_ConnHdlrPars ims_pars;
+ var IMS_ConnHdlr vc_conn_ims;
+ var AMI_Msg ami_msg;
+ const charstring c_ext_msisdn := "90829";
+
+ f_init();
+
+ ims_pars := f_init_IMS_ConnHdlrPars();
+
+ ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(c_ext_msisdn)));
+ ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(ims_pars.subscr.msisdn)));
+
+ vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline), ims_pars);
+
+ f_AMI_IMS_register(ims_pars);
+ IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims;
+
+ IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims;
+
+ ami_msg := f_ami_wait_rx_msg(AMI_CLIENT,
+ tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration),
+ fail_others := false);
+ g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL));
+
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Up", fail_others := false);
+
+ /* Notify network released dedicated bearer: */
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED) from vc_conn_ims;
+ AMI_CLIENT.clear;
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Down", fail_others := false);
+
+ /* Trigger unregistration: */
+ f_sleep(1.0);
+ AMI_CLIENT.clear;
+ f_AMI_IMS_unregister(ims_pars);
+
+ vc_conn_ims.done;
+ f_shutdown();
+}
+
+/* Validate incoming IMS MT call is rejected if no UAs are available due to being busy in an internall call. */
+testcase TC_ims_call_mt_local_uas_inacall() runs on test_CT {
+ var SIPConnHdlrPars sip_pars[2];
+ var IMS_ConnHdlrPars ims_pars;
+ var SIPConnHdlr vc_conn_sip[2];
+ var IMS_ConnHdlr vc_conn_ims;
+ var AMI_Msg ami_msg;
+ const charstring c_ext_msisdn := "90829";
+
+ f_init();
+
+ sip_pars[0] := f_init_ConnHdlrPars(idx := 1);
+ sip_pars[1] := f_init_ConnHdlrPars(idx := 2);
+ ims_pars := f_init_IMS_ConnHdlrPars();
+
+ sip_pars[0].cp.calling := sip_pars[0].registrar_sip_record;
+ sip_pars[0].cp.called := sip_pars[1].registrar_sip_record;
+
+ sip_pars[1].cp.calling := sip_pars[0].registrar_sip_record;
+ sip_pars[1].cp.called := sip_pars[1].local_sip_record;
+
+ ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(c_ext_msisdn)));
+ ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
+ ts_UserInfo(ims_pars.subscr.msisdn)));
+
+ vc_conn_sip[0] := f_start_handler(refers(f_TC_internal_call_mo), sip_pars[0]);
+ vc_conn_sip[1] := f_start_handler(refers(f_TC_internal_call_mt), sip_pars[1]);
+
+ vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline), ims_pars);
+ f_AMI_IMS_register(ims_pars);
+
+ interleave {
+ [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip[0];
+ [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip[1];
+ [] IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims;
+ }
+
+ COORD.send(COORD_CMD_START) to vc_conn_sip[0];
+
+ interleave {
+ [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip[0];
+ [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip[1];
+ }
+
+ /* Call on-going, now IMS MT call is attempted: */
+ f_sleep(1.0);
+ IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims;
+ ami_msg := f_ami_wait_rx_msg(AMI_CLIENT,
+ tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration),
+ fail_others := false);
+ g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL));
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Up", fail_others := false);
+ IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED) from vc_conn_ims;
+ AMI_CLIENT.clear;
+ f_ami_action_DedicatedBearerStatus(AMI_CLIENT,
+ g_asterisk_chan_name,
+ "Down", fail_others := false);
+
+
+ COORD.send(COORD_CMD_HANGUP) to vc_conn_sip[0];
+ COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip[1];
+
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip[0];
+ COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip[1];
+ AMI_CLIENT.clear;
+ f_AMI_IMS_unregister(ims_pars);
+
+ vc_conn_sip[0].done;
+ vc_conn_sip[1].done;
+ vc_conn_ims.done;
+ f_shutdown();
+}
+
+control {
+ execute( TC_internal_registration() );
+ execute( TC_internal_call_momt() );
+ execute( TC_internal_call_all_2registered() );
+ execute( TC_internal_call_all_3registered() );
+ execute( TC_internal_call_all_4registered() );
+ execute( TC_ims_registration() );
+ execute( TC_ims_registration_resync() );
+ execute( TC_ims_registration_timeout_initial_100Trying() );
+ execute( TC_ims_registration_timeout_initial_401Unauthorized() );
+ execute( TC_ims_registration_timeout_resync_401Unauthorized() );
+ execute( TC_ims_registration_timeout_protected_100Trying() );
+ execute( TC_ims_registration_timeout_protected_200OK() );
+ execute( TC_ims_registration_423_interval_too_brief() );
+ execute( TC_ims_reregistration() );
+ execute( TC_ims_call_mo() );
+ execute( TC_ims_call_mo_session_timer() );
+ execute( TC_ims_call_mo_noprecondition() );
+ execute( TC_ims_call_mo_holdresume_mo() );
+ execute( TC_ims_call_mo_holdswitchresume_mo() );
+ execute( TC_ims_call_mo_2nd_mo_rejected() );
+ execute( TC_ims_call_mo_2nd_mt_rejected() );
+ execute( TC_ims_call_mo_after_tcp_conn_closed() );
+ execute( TC_ims_call_mt() );
+ execute( TC_ims_call_mt_noprecondition() );
+ execute( TC_ims_call_mt_no_local_uas_registered() );
+ execute( TC_ims_call_mt_local_uas_inacall() );
+}
+
+}