Change in osmo-ttcn3-hacks[master]: WIP: MSC_Tests: Add SGs testcases

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Mon Jan 7 15:48:28 UTC 2019


Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/11488 )

Change subject: WIP: MSC_Tests: Add SGs testcases
......................................................................

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
---
M library/SGsAP_Emulation.ttcn
M library/SGsAP_Templates.ttcn
M msc/BSC_ConnectionHandler.ttcn
M msc/MSC_Tests.ttcn
M msc/expected-results.xml
M msc/gen_links.sh
M msc/regen_makefile.sh
7 files changed, 1,116 insertions(+), 14 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/library/SGsAP_Emulation.ttcn b/library/SGsAP_Emulation.ttcn
index 968bcdd..442cc7c 100644
--- a/library/SGsAP_Emulation.ttcn
+++ b/library/SGsAP_Emulation.ttcn
@@ -32,6 +32,7 @@
 import from Osmocom_Types all;
 import from IPL4asp_Types all;
 import from DNS_Helpers all;
+import from MobileL3_Types all;
 
 type component SGsAP_ConnHdlr {
 	port SGsAP_Conn_PT SGsAP;
@@ -41,7 +42,7 @@
 
 /* port between individual per-connection components and this dispatcher */
 type port SGsAP_Conn_PT message {
-	inout PDU_SGsAP;
+	inout PDU_SGsAP, PDU_ML3_MS_NW, PDU_ML3_NW_MS;
 } with { extension "internal" };
 
 /* represents a single SGsAP Association */
@@ -292,6 +293,8 @@
 
 	while (true) {
 		var SGsAP_ConnHdlr vc_conn;
+		var PDU_ML3_MS_NW l3_mo;
+		var PDU_ML3_NW_MS l3_mt;
 		var template IMSI imsi_t;
 		var hexstring imsi;
 		var SGsAP_RecvFrom mrf;
@@ -305,6 +308,38 @@
 			/* TODO: check which ConnectionID client has allocated + store in table? */
 			SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
 			}
+		/* DTAP/MobileL3 from client (emulated MS): wrap in SGsAP-UL-UD and send */
+		[] SGsAP_CLIENT.receive(PDU_ML3_MS_NW:?) -> value l3_mo sender vc_conn {
+			var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3_mo);
+			imsi := f_imsi_by_comp(vc_conn);
+			msg := valueof(ts_SGsAP_UL_UD(imsi, l3_enc));
+			SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
+			}
+		[] SGsAP_CLIENT.receive(PDU_ML3_NW_MS:?) -> value l3_mt sender vc_conn {
+			var octetstring l3_enc := enc_PDU_ML3_NW_MS(l3_mt);
+			imsi := f_imsi_by_comp(vc_conn);
+			msg := valueof(ts_SGsAP_DL_UD(imsi, l3_enc));
+			SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
+			}
+
+		/* DTAP/MobileL3 from MSC/VLR to MS wrapped in SGsAP-DL-UD: Extract, decode, forward */
+		[] SGsAP.receive(tr_SGsAP_RecvFrom_R(tr_SGsAP_DL_UD(?,?))) -> value mrf {
+			var octetstring l3_enc := mrf.msg.sGsAP_DOWNLINK_UNITDATA.nAS_MessageContainer.nAS_MessageContainer;
+			imsi := mrf.msg.sGsAP_DOWNLINK_UNITDATA.iMSI.iMSI.digits;
+			vc_conn := f_comp_by_imsi(imsi);
+			l3_mt := dec_PDU_ML3_NW_MS(l3_enc);
+			SGsAP_CLIENT.send(l3_mt) to vc_conn;
+			}
+		[] SGsAP.receive(tr_SGsAP_RecvFrom_R(tr_SGsAP_UL_UD(?,?))) -> value mrf {
+			var octetstring l3_enc := mrf.msg.sGsAP_UPLINK_UNITDATA.nAS_MessageContainer.nAS_MessageContainer;
+			imsi := mrf.msg.sGsAP_UPLINK_UNITDATA.iMSI.iMSI.digits;
+			l3_mo := dec_PDU_ML3_MS_NW(l3_enc);
+			vc_conn := f_comp_by_imsi(imsi);
+			SGsAP_CLIENT.send(l3_mo) to vc_conn;
+			}
+
+
+		/* any other SGsAP from MSC/VLR */
 		[] SGsAP.receive(tr_SGsAP_RecvFrom_R(?)) -> value mrf {
 			imsi_t := f_SGsAP_get_imsi(mrf.msg);
 			if (isvalue(imsi_t)) {
diff --git a/library/SGsAP_Templates.ttcn b/library/SGsAP_Templates.ttcn
index 9526080..457481e 100644
--- a/library/SGsAP_Templates.ttcn
+++ b/library/SGsAP_Templates.ttcn
@@ -69,7 +69,10 @@
 		return omit;
 	} else if (istemplatekind(digits, "*")) {
 		return *;
+	} else if (istemplatekind(digits, "?")) {
+		return ?;
 	}
+	log("tr_SGsAP_IMSI: ", digits);
 	var template IMSI imsi := {
 		iEI := '00000001'B,
 		lengthIndicator := lengthof(digits)/2 + 1,
diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn
index 43a1190..735cd21 100644
--- a/msc/BSC_ConnectionHandler.ttcn
+++ b/msc/BSC_ConnectionHandler.ttcn
@@ -39,8 +39,10 @@
 import from TELNETasp_PortType all;
 import from Osmocom_VTY_Functions all;
 
+import from SGsAP_Emulation all;
+
 /* this component represents a single subscriber connection */
-type component BSC_ConnHdlr extends BSSAP_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, MGCP_ConnHdlr, SMPP_ConnHdlr, CTRL_Adapter_CT {
+type component BSC_ConnHdlr extends BSSAP_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, MGCP_ConnHdlr, SMPP_ConnHdlr, CTRL_Adapter_CT, SGsAP_ConnHdlr {
 	var BSC_ConnHdlrPars g_pars;
 	timer g_Tguard := 60.0;
 	port TELNETasp_PT MSCVTY;
@@ -135,6 +137,8 @@
 	activate(as_Tguard());
 	/* Route all SMPP messages for our MSISDN to us */
 	f_create_smpp_expect(hex2str(pars.msisdn));
+	/* Route all SGs message for our IMSI to us */
+	f_create_sgsap_expect(pars.imsi);
 
 	if (g_pars.ipa_ctrl_enable == true) {
 		f_ipa_ctrl_start(g_pars.ipa_ctrl_ip, g_pars.ipa_ctrl_port);
@@ -452,21 +456,22 @@
 	mgcp_connection_id_mss := '0'H //
 };
 
-function f_mt_call_establish(inout CallParameters cpars)
+/* Allocate a call reference and send SETUP via MNCC to MSC */
+function f_mt_call_initate(inout CallParameters cpars)
 runs on BSC_ConnHdlr {
+	cpars.mncc_callref := f_rnd_int(2147483648);
+	MNCC.send(ts_MNCC_SETUP_req(cpars.mncc_callref, hex2str(g_pars.msisdn),
+					hex2str(cpars.called_party), hex2str(g_pars.imsi)));
+}
 
+/* Complete call, begin with a paging response message via BSSAP */
+function f_mt_call_complete(inout CallParameters cpars)
+runs on BSC_ConnHdlr {
 	var MNCC_PDU mncc;
 	var MgcpCommand mgcp_cmd;
 
 	f_bssmap_register_imsi(g_pars.imsi, g_pars.tmsi);
 
-	/* Allocate a call reference and send SETUP via MNCC to MSC */
-	cpars.mncc_callref := f_rnd_int(2147483648);
-	MNCC.send(ts_MNCC_SETUP_req(cpars.mncc_callref, hex2str(g_pars.msisdn),
-					hex2str(cpars.called_party), hex2str(g_pars.imsi)));
-	/* BSC <- MSC: Expect paging. FIXME: By TMSI or not? */
-	BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi));
-
 	f_establish_fully(EST_TYPE_PAG_RESP);
 
 	/* MS <- MSC: Expect CC SETUP */
@@ -476,7 +481,6 @@
 	BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_ALERTING(cpars.transaction_id)));
 	MNCC.receive(tr_MNCC_ALERT_ind(cpars.mncc_callref));
 
-
 	/* Create MGCP expect */
 	f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
 	/* Ask MSC via MNCC to create the RTP socket on the MSC/MGW side */
@@ -531,6 +535,20 @@
 	/* MS -> MSC: ALERTING */
 	BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT(cpars.transaction_id)));
 	MNCC.receive(tr_MNCC_SETUP_cnf(cpars.mncc_callref));
+}
+
+function f_mt_call_establish(inout CallParameters cpars)
+runs on BSC_ConnHdlr {
+
+	/* Initiate the call via MNCC */
+	f_mt_call_initate(cpars);
+
+	/* BSC <- MSC: Expect paging. FIXME: By TMSI or not? */
+	f_bssmap_register_imsi(g_pars.imsi, g_pars.tmsi);
+	BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi));
+
+	/* Complete the call via BSSAP */
+	f_mt_call_complete(cpars);
 
 	setverdict(pass);
 }
@@ -1008,7 +1026,6 @@
 }
 
 
-
 }
 
 
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 at 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() );
diff --git a/msc/expected-results.xml b/msc/expected-results.xml
index b9c60c0..8a5822d 100644
--- a/msc/expected-results.xml
+++ b/msc/expected-results.xml
@@ -74,4 +74,23 @@
   <testcase classname='MSC_Tests' name='TC_cipher_complete_with_invalid_cipher' time='MASKED'/>
   <testcase classname='MSC_Tests' name='TC_lu_imsi_auth_tmsi_encr_3_1_log_msc_debug' time='MASKED'/>
   <testcase classname='MSC_Tests' name='TC_mo_cc_bssmap_clear' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_reset' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_lu' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_lu_imsi_reject' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_lu_and_nothing' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_expl_imsi_det_eps' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_expl_imsi_det_noneps' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_paging_rej' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_paging_subscr_rej' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_paging_ue_unr' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_paging_and_nothing' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_paging_and_lu' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_unexp_ud' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_unsol_ud' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_mt_sms' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_mo_sms' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_mt_sms_and_nothing' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_mt_sms_and_reject' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_bssap_lu_sgsap_lu_and_mt_call' time='MASKED'/>
+  <testcase classname='MSC_Tests' name='TC_sgsap_lu_and_mt_call' time='MASKED'/>
 </testsuite>
diff --git a/msc/gen_links.sh b/msc/gen_links.sh
index 5d73fea..117564e 100755
--- a/msc/gen_links.sh
+++ b/msc/gen_links.sh
@@ -94,7 +94,7 @@
 FILES+="MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunctDef.cc "
 FILES+="SMPP_CodecPort.ttcn SMPP_CodecPort_CtrlFunct.ttcn SMPP_CodecPort_CtrlFunctDef.cc SMPP_Emulation.ttcn SMPP_Templates.ttcn "
 FILES+="SS_Templates.ttcn SCCP_Templates.ttcn USSD_Helpers.ttcn "
-FILES+="SGsAP_Templates.ttcn "
+FILES+="SGsAP_Templates.ttcn SGsAP_CodecPort.ttcn SGsAP_CodecPort_CtrlFunct.ttcn SGsAP_CodecPort_CtrlFunctDef.cc SGsAP_Emulation.ttcn DNS_Helpers.ttcn "
 gen_links $DIR $FILES
 
 ignore_pp_results
diff --git a/msc/regen_makefile.sh b/msc/regen_makefile.sh
index 556d69e..5645fdd 100755
--- a/msc/regen_makefile.sh
+++ b/msc/regen_makefile.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-FILES="*.ttcn *.ttcnpp SCCP_EncDec.cc  SCTPasp_PT.cc  TCCConversion.cc TCCInterface.cc UD_PT.cc MNCC_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc SDP_EncDec.cc RTP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc RTP_CodecPort_CtrlFunctDef.cc MGCP_CodecPort_CtrlFunctDef.cc TELNETasp_PT.cc Native_FunctionDefs.cc SMPP_EncDec.cc SMPP_CodecPort_CtrlFunctDef.cc MAP_EncDec.cc SS_EncDec.cc TCCEncoding.cc *.c *.asn"
+FILES="*.ttcn *.ttcnpp SCCP_EncDec.cc  SCTPasp_PT.cc  TCCConversion.cc TCCInterface.cc UD_PT.cc MNCC_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc SDP_EncDec.cc RTP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc RTP_CodecPort_CtrlFunctDef.cc MGCP_CodecPort_CtrlFunctDef.cc TELNETasp_PT.cc Native_FunctionDefs.cc SMPP_EncDec.cc SMPP_CodecPort_CtrlFunctDef.cc MAP_EncDec.cc SS_EncDec.cc TCCEncoding.cc SGsAP_CodecPort_CtrlFunctDef.cc *.c *.asn"
 
 export CPPFLAGS_TTCN3="-DIPA_EMULATION_MGCP -DIPA_EMULATION_GSUP -DIPA_EMULATION_SCCP -DUSE_MTP3_DISTRIBUTOR"
 

-- 
To view, visit https://gerrit.osmocom.org/11488
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I38543c35a9e74cea276e58d1d7ef01ef07ffe858
Gerrit-Change-Number: 11488
Gerrit-PatchSet: 20
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Vadim Yanitskiy <axilirator at gmail.com>
Gerrit-CC: dexter <pmaier at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190107/072de23a/attachment.htm>


More information about the gerrit-log mailing list