WIP: MSC_Tests: Add SGs testcases

This extens MSC_Tests.ttcn with an initial set of SGs interface test
cases for RESET, LU, DETACH, PAGING, SMS and CSFB procedures

In particular the following testcases are added:

- TC_sgsap_reset: isolated reset procedure test
- TC_sgsap_lu: isolated location update with TMSI realloc
- TC_sgsap_lu_imsi_reject: location update, reject case
- TC_sgsap_lu_and_nothing: location update with failed TMSI realloc
- TC_sgsap_expl_imsi_det_eps: detach from EPS serveces
- TC_sgsap_expl_imsi_det_noneps: detach from non-EPS services
- TC_sgsap_paging_rej: isolated paging, reject case
- TC_sgsap_paging_subscr_rej: isolated paging, subscr rejects call
- TC_sgsap_paging_ue_unr: isolated paging, ue is unreachable
- TC_sgsap_paging_and_nothing: page, but don't respond
- TC_sgsap_paging_and_lu: check paging followed by an LU
- TC_sgsap_mt_sms: mobile terminated SMS through SGs Interface
- TC_sgsap_mo_sms: mobile originated SMS through SGs Interface
- TC_sgsap_mt_sms_and_nothing: trigger SMS, but don't respond to paging
- TC_sgsap_mt_sms_and_reject: trigger SMS, but reject paging
- TC_sgsap_unexp_ud: Send unexpected unitdata (SGs Association: NULL)
- TC_sgsap_unsol_ud: Send unsolicited unitdata (subscriber not in VLR)
- TC_bssap_lu_sgsap_lu_and_mt_call: LU on 2G, LU on SGs and CSFB call
- TC_sgsap_lu_and_mt_call: LU on SGs, and CSFB call

Change-Id: I38543c35a9e74cea276e58d1d7ef01ef07ffe858
Depends: osmo-msc I73359925fc1ca72b33a1466e6ac41307f2f0b11d
Related: OS#3645
diff --git a/msc/MSC_Tests.ttcn b/msc/MSC_Tests.ttcn
index ef626bf..bf703e6 100644
--- a/msc/MSC_Tests.ttcn
+++ b/msc/MSC_Tests.ttcn
@@ -42,6 +42,10 @@
 import from BSSMAP_Emulation all;
 import from BSC_ConnectionHandler all;
 
+import from SGsAP_Templates all;
+import from SGsAP_Types all;
+import from SGsAP_Emulation all;
+
 import from MobileL3_Types all;
 import from MobileL3_CommonIE_Types all;
 import from MobileL3_SMS_Types all;
@@ -57,10 +61,14 @@
 import from SS_Types all;
 import from SS_Templates all;
 import from USSD_Helpers all;
+import from DNS_Helpers all;
 
 const integer NUM_BSC := 2;
 type record of BSSAP_Configuration BSSAP_Configurations;
 
+/* Needed for SGsAP SMS */
+import from MobileL3_SMS_Types all;
+
 type component MTC_CT extends CTRL_Adapter_CT {
 	var boolean g_initialized := false;
 
@@ -72,6 +80,7 @@
 	var GSUP_Emulation_CT vc_GSUP;
 	var IPA_Emulation_CT vc_GSUP_IPA;
 	var SMPP_Emulation_CT vc_SMPP;
+	var SGsAP_Emulation_CT vc_SGsAP;
 
 	/* only to get events from IPA underneath GSUP */
 	port IPA_CTRL_PT GSUP_IPA_EVENT;
@@ -107,6 +116,8 @@
 	integer mp_msc_smpp_port := 2775;
 	charstring mp_smpp_system_id := "msc_tester";
 	charstring mp_smpp_password := "osmocom1";
+	charstring mp_mme_name := "mmec01.mmegi0001.mme.epc.mnc070.mcc901.3gppnetwork.org";
+	charstring mp_vlr_name := "vlr.example.net";
 
 	BSSAP_Configurations mp_bssap_cfg := {
 		{
@@ -193,6 +204,25 @@
 	vc_MGCP.start(MGCP_Emulation.main(ops, pars, id));
 }
 
+function f_init_sgsap(charstring id) runs on MTC_CT {
+	id := id & "-SGsAP";
+	var SGsAPOps ops := {
+		create_cb := refers(SGsAP_Emulation.ExpectedCreateCallback),
+		unitdata_cb := refers(SGsAP_Emulation.DummyUnitdataCallback)
+	}
+	var SGsAP_conn_parameters pars := {
+		remote_ip := mp_msc_ip,
+		remote_sctp_port := 29118,
+		local_ip := "",
+		local_sctp_port := -1
+	}
+
+	vc_SGsAP := SGsAP_Emulation_CT.create(id);
+	map(vc_SGsAP:SGsAP, system:SGsAP_CODEC_PT);
+	vc_SGsAP.start(SGsAP_Emulation.main(ops, pars, id));
+}
+
+
 function f_init_gsup(charstring id) runs on MTC_CT {
 	id := id & "-GSUP";
 	var GsupOps ops := {
@@ -247,6 +277,7 @@
 	f_init_mgcp("MSC_Test");
 	f_init_gsup("MSC_Test");
 	f_init_smpp("MSC_Test");
+	f_init_sgsap("MSC_Test");
 
 	map(self:MSCVTY, system:MSCVTY);
 	f_vty_set_prompts(MSCVTY);
@@ -485,6 +516,9 @@
 	/* SMPP part */
 	connect(vc_conn:SMPP, vc_SMPP:SMPP_CLIENT);
 	connect(vc_conn:SMPP_PROC, vc_SMPP:SMPP_PROC);
+	/* SGs part */
+	connect(vc_conn:SGsAP, vc_SGsAP:SGsAP_CLIENT);
+	connect(vc_conn:SGsAP_PROC, vc_SGsAP:SGsAP_PROC);
 
 	/* We cannot use vc_conn.start(f_init_handler(fn, id, pars)); as we cannot have
 	 * a stand-alone 'derefers()' call, see https://www.eclipse.org/forums/index.php/t/1091364/ */
@@ -2985,6 +3019,980 @@
    * too long / short TLV values
  */
 
+/* Perform a location updatye at the A-Interface and run some checks to confirm
+ * that everything is back to normal. */
+private function f_sgsap_bssmap_screening()  runs on BSC_ConnHdlr {
+	var SmsParameters spars := valueof(t_SmsPars);
+
+	/* Perform a location update, the SGs association is expected to fall
+	 * back to NULL */
+	f_perform_lu();
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	/* Trigger a paging request and expect the paging on BSSMAP, this is
+	 * to make sure that pagings are sent throught the A-Interface again
+	 * and not throught the SGs interface.*/
+	f_bssmap_register_imsi(g_pars.imsi, g_pars.tmsi);
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+
+	alt {
+	[] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)); {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Send an SMS to make sure that also payload messages are routed
+	 * throught the A-Interface again */
+	f_establish_fully(EST_TYPE_MO_SMS);
+	f_mo_sms(spars);
+	f_expect_clear();
+}
+
+private function f_tc_sgsap_reset(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	var charstring vlr_name;
+	f_init_handler(pars);
+
+	vlr_name := f_sgsap_reset_mme(mp_mme_name);
+	log("VLR name: ", vlr_name);
+	setverdict(pass);
+}
+
+testcase TC_sgsap_reset() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_reset), 10);
+	vc_conn.done;
+}
+
+/* like f_mm_auth() but for SGs */
+function f_mm_auth_sgs() runs on BSC_ConnHdlr {
+	if (g_pars.net.expect_auth) {
+		g_pars.vec := f_gen_auth_vec_3g();
+		var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G3G(g_pars.vec.rand,
+									   g_pars.vec.sres,
+									   g_pars.vec.kc,
+									   g_pars.vec.ik,
+									   g_pars.vec.ck,
+									   g_pars.vec.autn,
+									   g_pars.vec.res));
+		GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi));
+		GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
+		SGsAP.receive(tr_ML3_MT_MM_AUTH_REQ_3G(g_pars.vec.rand, g_pars.vec.autn));
+		SGsAP.send(ts_ML3_MT_MM_AUTH_RESP_3G(g_pars.vec.sres, g_pars.vec.res));
+	}
+}
+
+/* like f_perform_lu(), but on SGs rather than BSSAP */
+function f_sgs_perform_lu() runs on BSC_ConnHdlr {
+	var octetstring mme_name := f_enc_dns_hostname(mp_mme_name);
+	var PDU_SGsAP lur;
+	var PDU_SGsAP lua;
+	var PDU_SGsAP mm_info;
+	var octetstring mm_info_dtap;
+
+	/* tell GSUP dispatcher to send this IMSI to us */
+	f_create_gsup_expect(hex2str(g_pars.imsi));
+
+	lur := valueof(ts_SGsAP_LU_REQ(g_pars.imsi, mme_name, IMSI_attach,
+					ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	/* Old LAI, if MS sends it */
+	/* TMSI status, if MS has no valid TMSI */
+	/* IMEISV, if it supports "automatic device detection" */
+	/* TAI, if available in MME */
+	/* E-CGI, if available in MME */
+	SGsAP.send(lur);
+
+	/* FIXME: is this really done over SGs?  The Ue is already authenticated
+	 * via the MME ... */
+	f_mm_auth_sgs();
+
+	/* Expect MSC to perform LU with HLR */
+	GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
+	GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn));
+	GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi));
+	GSUP.send(ts_GSUP_UL_RES(g_pars.imsi));
+
+	alt {
+	[] SGsAP.receive(tr_SGsAP_LU_ACCEPT(g_pars.imsi, ?)) -> value lua {
+		if (isvalue(lua.sGsAP_LOCATION_UPDATE_ACCEPT.newTMSIorIMSI.iD.iD.tmsi_ptmsi.octets)) {
+			g_pars.tmsi :=lua.sGsAP_LOCATION_UPDATE_ACCEPT.newTMSIorIMSI.iD.iD.tmsi_ptmsi.octets
+			SGsAP.send(ts_SGsAP_TMSI_REALL_CMPL(g_pars.imsi));
+		}
+		setverdict(pass);
+		}
+	[] SGsAP.receive(tr_SGsAP_LU_REJECT(g_pars.imsi, ?, ?)) {
+		setverdict(fail, "Received LU-REJECT instead of ACCEPT");
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Check MM information */
+	if (mp_mm_info == true) {
+		SGsAP.receive(tr_SGsAP_MM_INFO_REQ(g_pars.imsi, ?)) -> value mm_info;
+		mm_info_dtap := '0532'O & mm_info.sGsAP_MM_INFORMATION_REQUEST.mM_Information.information;
+		if (not match(dec_PDU_ML3_NW_MS(mm_info_dtap), tr_ML3_MT_MM_Info)) {
+			setverdict(fail, "Unexpected MM Information");
+		}
+	}
+
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+}
+
+private function f_tc_sgsap_lu(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_lu() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_lu), 10);
+	vc_conn.done;
+}
+
+/* Do LU by IMSI, refuse it on GSUP and expect LU REJ back to MS */
+private function f_tc_sgsap_lu_imsi_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	var PDU_SGsAP lur;
+
+	f_create_gsup_expect(hex2str(g_pars.imsi));
+	var octetstring mme_name := f_enc_dns_hostname(mp_mme_name);
+	lur := valueof(ts_SGsAP_LU_REQ(g_pars.imsi, mme_name, IMSI_attach,
+					ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	SGsAP.send(lur);
+
+	GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
+	GSUP.send(ts_GSUP_UL_ERR(g_pars.imsi, 23));
+	alt {
+	[] SGsAP.receive(tr_SGsAP_LU_REJECT(g_pars.imsi, ?, ?)) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive(tr_SGsAP_LU_ACCEPT(g_pars.imsi, ?)) {
+		setverdict(fail, "Expecting LU REJ, but got ACCEPT");
+		mtc.stop;
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_lu_imsi_reject() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+
+	vc_conn := f_start_handler(refers(f_tc_sgsap_lu_imsi_reject), 3);
+	vc_conn.done;
+}
+
+/* Do LU by IMSI, but then remain silent so that Ts6-1 times out */
+private function f_tc_sgsap_lu_and_nothing(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	var octetstring mme_name := f_enc_dns_hostname(mp_mme_name);
+	var PDU_SGsAP lur;
+
+	f_init_handler(pars);
+
+	/* tell GSUP dispatcher to send this IMSI to us */
+	f_create_gsup_expect(hex2str(g_pars.imsi));
+
+	lur := valueof(ts_SGsAP_LU_REQ(g_pars.imsi, mme_name, IMSI_attach,
+					ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	/* Old LAI, if MS sends it */
+	/* TMSI status, if MS has no valid TMSI */
+	/* IMEISV, if it supports "automatic device detection" */
+	/* TAI, if available in MME */
+	/* E-CGI, if available in MME */
+	SGsAP.send(lur);
+
+	/* FIXME: is this really done over SGs?  The Ue is already authenticated
+	 * via the MME ... */
+	f_mm_auth_sgs();
+
+	/* Expect MSC to perform LU with HLR */
+	GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
+	GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn));
+	GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi));
+	GSUP.send(ts_GSUP_UL_RES(g_pars.imsi));
+
+	alt {
+	[] SGsAP.receive(tr_SGsAP_LU_ACCEPT(g_pars.imsi, ?)) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive(tr_SGsAP_LU_REJECT(g_pars.imsi, ?, ?)) {
+		setverdict(fail, "Received LU-REJECT instead of ACCEPT");
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	/* Wait until the VLR has abort the TMSI reallocation procedure */
+	f_sleep(45.0);
+
+	/* The outcome does not change the SGs state, see also 5.2.3.4 */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_lu_and_nothing() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+
+	vc_conn := f_start_handler(refers(f_tc_sgsap_lu_and_nothing), 3);
+	vc_conn.done;
+}
+
+private function f_tc_sgsap_expl_imsi_det_eps(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(3.0);
+
+	var octetstring mme_name := f_enc_dns_hostname(mp_mme_name);
+	SGsAP.send(ts_SGsAP_EPS_DETACH_IND(g_pars.imsi, mme_name, UE_initiated));
+	SGsAP.receive(tr_SGsAP_EPS_DETACH_ACK(g_pars.imsi));
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_expl_imsi_det_eps() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_expl_imsi_det_eps), 10);
+	vc_conn.done;
+}
+
+private function f_tc_sgsap_expl_imsi_det_noneps(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(3.0);
+
+	var octetstring mme_name := f_enc_dns_hostname(mp_mme_name);
+	SGsAP.send(ts_SGsAP_IMSI_DETACH_IND(g_pars.imsi, mme_name, combined_UE_initiated));
+	SGsAP.receive(tr_SGsAP_IMSI_DETACH_ACK(g_pars.imsi));
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+	/* FIXME: How to verify that VLR has removed MM context? */
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_expl_imsi_det_noneps() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_expl_imsi_det_noneps), 1081);
+	vc_conn.done;
+}
+
+/* Trigger a paging request via VTY and send a paging reject in response */
+private function f_tc_sgsap_paging_rej(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_resp := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, CS_call_indicator, omit);
+	var template  LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_resp.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Initiate paging via VTY */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(exp_resp) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Now reject the paging */
+	SGsAP.send(ts_SGsAP_PAGING_REJ(g_pars.imsi, IMSI_unknown));
+
+	/* Wait for the states inside the MSC to settle and check the state
+	 * of the SGs Association */
+	f_sleep(1.0);
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	/* FIXME: At the moment we send an IMSI_unknown as cause code, which is fine,
+	 * but we also need to cover tha case where the cause code indicates an
+	 * "IMSI detached for EPS services". In those cases the VLR is expected to
+	 * try paging on tha A/Iu interface. This will be another testcase similar to
+	 * this one, but extended with checks for the presence of the A/Iu paging
+	 * messages. */
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_paging_rej() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_paging_rej), 1082);
+	vc_conn.done;
+}
+
+/* Trigger a paging request via VTY and send a paging reject that indicates
+ * that the subscriber intentionally rejected the call. */
+private function f_tc_sgsap_paging_subscr_rej(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_resp := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, CS_call_indicator, omit);
+	var template  LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_resp.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Initiate paging via VTY */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(exp_resp) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Now reject the paging */
+	SGsAP.send(ts_SGsAP_PAGING_REJ(g_pars.imsi, user_rejected_mobile_terminating_CS_fallback_call));
+
+	/* Wait for the states inside the MSC to settle and check the state
+	 * of the SGs Association */
+	f_sleep(1.0);
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	/* FIXME: The VLR is supposed to trigger an User Determined User Busy (UDUB) as specified
+	 * in 3GPP TS 24.082, this is not yet implemented in the MSC or in this tests, we need
+	 * to check back how this works and how it can be tested */
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_paging_subscr_rej() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_paging_subscr_rej), 1083);
+	vc_conn.done;
+}
+
+/* Trigger a paging request via VTY and send an UE unreacable messge in response */
+private function f_tc_sgsap_paging_ue_unr(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_resp := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, CS_call_indicator, omit);
+	var template  LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_resp.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Initiate paging via VTY */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(exp_resp) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Now pretend that the UE is unreachable */
+	SGsAP.send(ts_SGsAP_UE_UNREACHABLE(g_pars.imsi, UE_unreachable));
+
+	/* Wait for the states inside the MSC to settle and check the state
+	 * of the SGs Association. */
+	f_sleep(1.0);
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_paging_ue_unr() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_paging_ue_unr), 10);
+	vc_conn.done;
+}
+
+/* Trigger a paging request via VTY but don't respond to it */
+private function f_tc_sgsap_paging_and_nothing(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_resp := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, CS_call_indicator, omit);
+	var template  LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_resp.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Initiate paging via VTY */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(exp_resp) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Now do nothing, the MSC/VLR should fail silently to page after a
+	 * few seconds, The SGs association must remain unchanged. */
+	f_sleep(15.0);
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_paging_and_nothing() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_paging_and_nothing), 92);
+	vc_conn.done;
+}
+
+/* Trigger a paging request via VTY and slip in an LU */
+private function f_tc_sgsap_paging_and_lu(charstring id, BSC_ConnHdlrPars pars)
+runs on BSC_ConnHdlr {
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	f_init_handler(pars);
+
+	/* First we prepar the situation, where the SGs association is in state
+	 * NULL and the confirmed by radio contact indicator is set to false
+	 * as well. This can be archived by performing an SGs LU and then
+	 * resetting the VLR */
+	f_sgs_perform_lu();
+	f_sgsap_reset_mme(mp_mme_name);
+	f_sleep(1.0);
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	/* Perform a paging, expect the paging messages on the SGs interface */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, CS_call_indicator, omit)) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Perform the LU as normal */
+	f_sgs_perform_lu();
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	/* Expect a new paging request right after the LU */
+	alt {
+	[] SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, CS_call_indicator, omit)) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	/* Test is done now, lets round everything up by rejecting the paging
+	 * cleanly. */
+	SGsAP.send(ts_SGsAP_PAGING_REJ(g_pars.imsi, user_rejected_mobile_terminating_CS_fallback_call));
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_paging_and_lu() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_paging_and_lu), 9792);
+	vc_conn.done;
+}
+
+/* Send unexpected unit-data through the SGs interface */
+private function f_tc_sgsap_unexp_ud(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sleep(1.0);
+
+	/* This simulates what happens when a subscriber without SGs
+	 * association gets unitdata via the SGs interface. */
+
+	/* Make sure the subscriber exists and the SGs association
+	 * is in NULL state */
+	f_perform_lu();
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	/* Send some random unit data, the MSC/VLR should send a release
+	 * immediately. */
+	SGsAP.send(ts_SGsAP_UL_UD(pars.imsi,'1234'O));
+	SGsAP.receive(tr_SGsAP_RELEASE_REQ(pars.imsi, IMSI_detached_for_EPS_nonEPS_services));
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_unexp_ud() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_unexp_ud), 2145);
+	vc_conn.done;
+}
+
+/* Send unsolicited unit-data through the SGs interface */
+private function f_tc_sgsap_unsol_ud(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sleep(1.0);
+
+	/* This simulates what happens when the MME attempts to send unitdata
+	 * to a subscriber that is completely unknown to the VLR */
+
+	/* Send some random unit data, the MSC/VLR should send a release
+	 * immediately. */
+	SGsAP.send(ts_SGsAP_UL_UD(pars.imsi,'1234'O));
+	SGsAP.receive(tr_SGsAP_RELEASE_REQ(pars.imsi, IMSI_unknown));
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_unsol_ud() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_unsol_ud), 146);
+	vc_conn.done;
+}
+
+private altstep as_other_sms_sgs() runs on BSC_ConnHdlr {
+	/* FIXME: Match an actual payload (second questionmark), the type is
+	 * octetstring, how do we use a tr_PDU_DTAP_MT here? */
+	[] SGsAP.receive(tr_SGsAP_DL_UD(?,?)) {
+		setverdict(fail, "Unexpected SMS related PDU from MSC");
+		mtc.stop;
+	}
+}
+
+/* receive a MT-SMS delivered from the MSC/SMSC over an already existing SGsAP connection */
+function f_mt_sms_sgs(inout SmsParameters spars)
+runs on BSC_ConnHdlr {
+	var template (value) TPDU_RP_DATA_MS_SGSN tp_mo;
+	var template (value) RPDU_MS_SGSN rp_mo;
+	var template (value) PDU_ML3_MS_NW l3_mo;
+
+	var template TPDU_RP_DATA_SGSN_MS tp_mt;
+	var template RPDU_SGSN_MS rp_mt;
+	var template PDU_ML3_NW_MS l3_mt;
+
+	var PDU_ML3_NW_MS sgsap_l3_mt;
+
+	var default d := activate(as_other_sms_sgs());
+
+	/* Expect CP-DATA(RP-DATA(SMS-DELIVER)) */
+	tp_mt := tr_SMS_DELIVER(?, spars.tp.ud, spars.tp.pid, spars.tp.dcs, ?);
+	rp_mt := tr_RP_DATA_MT(?, ?, omit, tp_mt);
+	l3_mt := tr_ML3_MT_SMS(?, c_TIF_ORIG, tr_CP_DATA_MT(rp_mt));
+
+	SGsAP.receive(l3_mt) -> value sgsap_l3_mt;
+
+	/* Extract relevant identifiers */
+	spars.tid := bit2int(sgsap_l3_mt.tiOrSkip.transactionId.tio);
+	spars.rp.msg_ref := sgsap_l3_mt.msgs.sms.cP_DATA.cP_User_Data.cP_RPDU.rP_DATA_SGSN_MS.rP_MessageReference;
+
+	/* send CP-ACK for CP-DATA just received */
+	l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_REPL, ts_CP_ACK_MO);
+
+	SGsAP.send(l3_mo);
+
+	/* send RP-ACK for RP-DATA */
+	rp_mo := ts_RP_ACK_MO(spars.rp.msg_ref);
+	l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_REPL, ts_CP_DATA_MO(rp_mo));
+
+	SGsAP.send(l3_mo);
+
+	/* expect CP-ACK for CP-DATA(RP-ACK) just sent */
+	l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_ORIG, tr_CP_ACK_MT);
+
+	SGsAP.receive(l3_mt);
+
+	deactivate(d);
+
+	setverdict(pass);
+}
+
+/* submit a MO-SMS to MSC/SMSC over an already existing SGsAP connection */
+function f_mo_sms_sgs(inout SmsParameters spars)
+runs on BSC_ConnHdlr {
+	var template (value) TPDU_RP_DATA_MS_SGSN tp_mo;
+	var template (value) RPDU_MS_SGSN rp_mo;
+	var template (value) PDU_ML3_MS_NW l3_mo;
+
+	var template TPDU_RP_DATA_SGSN_MS tp_mt;
+	var template RPDU_SGSN_MS rp_mt;
+	var template PDU_ML3_NW_MS l3_mt;
+
+	var default d := activate(as_other_sms_sgs());
+
+	/* just in case this is routed to SMPP.. */
+	f_create_smpp_expect(hex2str(spars.tp.da.tP_DA_NoPad.tP_DAValue));
+
+	tp_mo := ts_SMS_SUBMIT(spars.tp.msg_ref, spars.tp.da, spars.tp.pid, spars.tp.dcs,
+				 spars.tp.udl, spars.tp.ud);
+	rp_mo := ts_RP_DATA_MO(spars.rp.msg_ref, spars.rp.orig, spars.rp.dest, tp_mo);
+	l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_DATA_MO(rp_mo));
+
+	SGsAP.send(l3_mo);
+
+	/* receive CP-ACK for CP-DATA above */
+	SGsAP.receive(tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_ACK_MT));
+
+	if (ispresent(spars.exp_rp_err)) {
+		/* expect an RP-ERROR message from MSC with given cause */
+		rp_mt := tr_RP_ERROR_MT(spars.rp.msg_ref, spars.exp_rp_err);
+		l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_DATA_MT(rp_mt));
+		SGsAP.receive(l3_mt);
+		/* send CP-ACK for CP-DATA just received */
+		l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_ACK_MO);
+		SGsAP.send(l3_mo);
+	} else {
+		/* expect RP-ACK for RP-DATA */
+		rp_mt := tr_RP_ACK_MT(spars.rp.msg_ref);
+		l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_DATA_MT(rp_mt));
+		SGsAP.receive(l3_mt);
+		/* send CP-ACO for CP-DATA just received */
+		l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_ACK_MO);
+		SGsAP.send(l3_mo);
+	}
+
+	deactivate(d);
+
+	setverdict(pass);
+}
+
+private function f_vty_sms_send_conn_hdlr(charstring imsi, charstring msisdn, charstring text)
+runs on BSC_ConnHdlr {
+	f_vty_transceive(MSCVTY, "subscriber imsi "&imsi&" sms sender msisdn "&msisdn&" send "&text);
+}
+
+/* Send a MT SMS via SGs interface */
+private function f_tc_sgsap_mt_sms(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+	var SmsParameters spars := valueof(t_SmsPars);
+	spars.tp.ud := 'C8329BFD064D9B53'O;
+
+	/* Trigger SMS via VTY */
+	f_vty_sms_send_conn_hdlr(hex2str(pars.imsi), "2342", "Hello SMS");
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+
+	/* Expect a paging request and respond accordingly with a service request */
+	SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, SMS_indicator, omit));
+	SGsAP.send(ts_SGsAP_SERVICE_REQ(pars.imsi, SMS_indicator, EMM_CONNECTED));
+
+	/* Connection is now live, receive the MT-SMS */
+	f_mt_sms_sgs(spars);
+
+	/* Expect a concluding release from the MSC */
+	SGsAP.receive(tr_SGsAP_RELEASE_REQ(pars.imsi, omit));
+
+	/* Make sure that subscriber is still present and the SGs association is in tact (ref-counting) */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+testcase TC_sgsap_mt_sms() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_mt_sms), 1145);
+	vc_conn.done;
+}
+
+/* Send a MO SMS via SGs interface */
+private function f_tc_sgsap_mo_sms(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+	var SmsParameters spars := valueof(t_SmsPars);
+	spars.tp.ud := 'C8329BFD064D9B53'O;
+
+	/* Send the MO-SMS */
+	f_mo_sms_sgs(spars);
+
+	/* Expect a concluding release from the MSC/VLR */
+	SGsAP.receive(tr_SGsAP_RELEASE_REQ(pars.imsi, omit));
+
+	/* Make sure that subscriber is still present and the SGs association is in tact (ref-counting) */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	setverdict(pass);
+
+	f_sgsap_bssmap_screening()
+}
+testcase TC_sgsap_mo_sms() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_mo_sms), 3145);
+	vc_conn.done;
+}
+
+/* Trigger sending of an MT sms via VTY but never respond to anything  */
+private function f_tc_sgsap_mt_sms_and_nothing(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars, 170.0);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var SmsParameters spars := valueof(t_SmsPars);
+	spars.tp.ud := 'C8329BFD064D9B53'O;
+	var integer page_count := 0;
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_pag_req := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, SMS_indicator, omit);
+	var template LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_pag_req.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Trigger SMS via VTY */
+	f_vty_sms_send_conn_hdlr(hex2str(pars.imsi), "2342", "Hello SMS");
+
+	/* Expect the MSC/VLR to page exactly 10 times before giving up */
+	alt {
+		[] SGsAP.receive(exp_pag_req)
+		{
+			page_count := page_count + 1;
+
+			if (page_count < 10) {
+				repeat;
+			}
+		}
+		[] SGsAP.receive {
+			setverdict(fail, "unexpected SGsAP message received");
+			self.stop;
+		}
+	}
+
+	/* Wait some time to make sure the MSC is not delivering any further
+	 * paging messages or anything else that could be unexpected. */
+	timer T := 20.0;
+	T.start
+	alt {
+		[] SGsAP.receive(exp_pag_req)
+		{
+			setverdict(fail, "paging seems not to stop!");
+			mtc.stop;
+		}
+		[] SGsAP.receive {
+			setverdict(fail, "unexpected SGsAP message received");
+			self.stop;
+		}
+		[] T.timeout {
+			setverdict(pass);
+		}
+	}
+
+	/* Even on a failed paging the SGs Association should stay intact */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	/* Note: We do not execute f_sgsap_bssmap_screening() here since the
+	 * MSC/VLR would re-try to deliver the test SMS trigered above and
+	 * so the screening would fail. */
+
+	/* Expire the subscriber now to avoid that the MSC will try the SMS
+	 * delivery at some later point. */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " expire");
+
+	setverdict(pass);
+}
+testcase TC_sgsap_mt_sms_and_nothing() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_mt_sms_and_nothing), 4581);
+	vc_conn.done;
+}
+
+/* Trigger sending of an MT sms via VTY but reject the paging immediately */
+private function f_tc_sgsap_mt_sms_and_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_init_handler(pars, 150.0);
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var SmsParameters spars := valueof(t_SmsPars);
+	spars.tp.ud := 'C8329BFD064D9B53'O;
+	var integer page_count := 0;
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var template PDU_SGsAP exp_pag_req := tr_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, SMS_indicator, omit);
+	var template LocationAreaId exp_lai := ts_SGsAP_IE_Lai(valueof(ts_SGsAP_LAI('901'H, '70'H, 2342)));
+	exp_pag_req.sGsAP_PAGING_REQUEST.locationAreaId := exp_lai;
+
+	/* Trigger SMS via VTY */
+	f_vty_sms_send_conn_hdlr(hex2str(pars.imsi), "2342", "Hello SMS");
+
+	/* Expect a paging request and reject it immediately */
+	SGsAP.receive(exp_pag_req);
+	SGsAP.send(ts_SGsAP_PAGING_REJ(g_pars.imsi, IMSI_unknown));
+
+	/* The MSC/VLR should no longer try to page once the paging has been
+	 * rejected. Wait some time and check if there are no unexpected
+	 * messages on the SGs interface. */
+	timer T := 20.0;
+	T.start
+	alt {
+		[] SGsAP.receive(exp_pag_req)
+		{
+			setverdict(fail, "paging seems not to stop!");
+			mtc.stop;
+		}
+		[] SGsAP.receive {
+			setverdict(fail, "unexpected SGsAP message received");
+			self.stop;
+		}
+		[] T.timeout {
+			setverdict(pass);
+		}
+	}
+
+	/* A rejected paging with IMSI_unknown (see above) should always send
+	 * the SGs association to NULL. */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-NULL");
+
+	f_sgsap_bssmap_screening();
+
+	/* Expire the subscriber now to avoid that the MSC will try the SMS
+	 * delivery at some later point. */
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " expire");
+
+	setverdict(pass);
+}
+testcase TC_sgsap_mt_sms_and_reject() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+	vc_conn := f_start_handler(refers(f_tc_sgsap_mt_sms_and_reject), 4145);
+	vc_conn.done;
+}
+
+/* Perform an MT CSDB call including LU */
+private function f_mt_lu_and_csfb_call(charstring id, BSC_ConnHdlrPars pars, boolean bssmap_lu) runs on BSC_ConnHdlr {
+	f_init_handler(pars);
+
+	/* Be sure that the BSSMAP reset is done before we begin. */
+	f_sleep(2.0);
+
+	/* Testcase variation: See what happens when we do a regular BSSMAP
+	 * LU first (this should not hurt in any way!) */
+	if (bssmap_lu) {
+		f_perform_lu();
+	}
+
+	f_sgs_perform_lu();
+	f_sleep(1.0);
+
+	var octetstring vlr_name := f_enc_dns_hostname(mp_vlr_name);
+	var CallParameters cpars := valueof(t_CallParams('12345'H, 0));
+	cpars.bss_rtp_port := 1110;
+	cpars.mgcp_connection_id_bss := '10004'H;
+	cpars.mgcp_connection_id_mss := '10005'H;
+
+	/* Note: This is an optional parameter. When the call-agent (MSC) does
+	 * supply a full endpoint name this setting will be overwritten. */
+	cpars.mgcp_ep := "rtpbridge/1@mgw";
+
+	/* Initiate a call via MNCC interface */
+	f_mt_call_initate(cpars);
+
+	/* Expect a paging request and respond accordingly with a service request */
+	SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, CS_call_indicator, omit));
+	SGsAP.send(ts_SGsAP_SERVICE_REQ(pars.imsi, CS_call_indicator, EMM_CONNECTED));
+
+	/* Complete the call, hold it for some time and then tear it down */
+	f_mt_call_complete(cpars);
+	f_sleep(3.0);
+	f_call_hangup(cpars, true);
+
+	/* Make sure that subscriber is still present and the SGs association is in tact (ref-counting) */
+	f_ctrl_get_exp(IPA_CTRL, "fsm.SGs-UE.id.imsi:" & hex2str(g_pars.imsi) & ".state", "SGs-ASSOCIATED");
+
+	/* Finally simulate the return of the UE to the 4G network */
+	SGsAP.send(ts_SGsAP_MO_CSFB_IND(pars.imsi));
+
+	/* Test for successful return by triggering a paging, when the paging
+	 * request is received via SGs, we can be sure that the MSC/VLR has
+	 * recognized that the UE is now back on 4G */
+	f_sleep(1.0);
+	f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging");
+	alt {
+	[] SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, CS_call_indicator, omit)) {
+		setverdict(pass);
+		}
+	[] SGsAP.receive {
+		setverdict(fail, "Received unexpected message on SGs");
+		}
+	}
+
+	f_sgsap_bssmap_screening();
+
+	setverdict(pass);
+}
+
+/* Perform a regular BSSAP LU first, do a SGSAP LU and then make a CSFB call */
+private function f_tc_bssap_lu_sgsap_lu_and_mt_call(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_mt_lu_and_csfb_call(id, pars, true);
+}
+testcase TC_bssap_lu_sgsap_lu_and_mt_call() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+
+	vc_conn := f_start_handler(refers(f_tc_bssap_lu_sgsap_lu_and_mt_call), 139);
+	vc_conn.done;
+}
+
+
+/* Perform a SGSAP LU and then make a CSFB call */
+private function f_tc_sgsap_lu_and_mt_call(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+	f_mt_lu_and_csfb_call(id, pars, false);
+}
+testcase TC_sgsap_lu_and_mt_call() runs on MTC_CT {
+	var BSC_ConnHdlr vc_conn;
+	f_init();
+
+	vc_conn := f_start_handler(refers(f_tc_sgsap_lu_and_mt_call), 239);
+	vc_conn.done;
+}
+
+/* SGs TODO:
+   * LU attempt for IMSI without NAM_PS in HLR
+   * LU attempt with AUTH FAIL due to invalid RES/SRES
+   * LU attempt with no response from HLR (VLR should timeout + LU REJ)
+   * LU attempt with new TMSI but without TMSI REALL CMPL baco to VLR
+   * implicit IMSI detach from EPS
+   * implicit IMSI detach from non-EPS
+   * MM INFO
+   *
+ */
 
 control {
 	execute( TC_cr_before_reset() );
@@ -3051,6 +4059,26 @@
 
 	execute( TC_cipher_complete_with_invalid_cipher() );
 
+	execute( TC_sgsap_reset() );
+	execute( TC_sgsap_lu() );
+	execute( TC_sgsap_lu_imsi_reject() );
+	execute( TC_sgsap_lu_and_nothing() );
+	execute( TC_sgsap_expl_imsi_det_eps() );
+	execute( TC_sgsap_expl_imsi_det_noneps() );
+	execute( TC_sgsap_paging_rej() );
+	execute( TC_sgsap_paging_subscr_rej() );
+	execute( TC_sgsap_paging_ue_unr() );
+	execute( TC_sgsap_paging_and_nothing() );
+	execute( TC_sgsap_paging_and_lu() );
+	execute( TC_sgsap_mt_sms() );
+	execute( TC_sgsap_mo_sms() );
+	execute( TC_sgsap_mt_sms_and_nothing() );
+	execute( TC_sgsap_mt_sms_and_reject() );
+	execute( TC_sgsap_unexp_ud() );
+	execute( TC_sgsap_unsol_ud() );
+	execute( TC_bssap_lu_sgsap_lu_and_mt_call() );
+	execute( TC_sgsap_lu_and_mt_call() );
+
 	/* Run this last: at the time of writing this test crashes the MSC */
 	execute( TC_lu_imsi_auth_tmsi_encr_3_1_log_msc_debug() );
 	execute( TC_mo_cc_bssmap_clear() );