mme: Trigger last parts of procedure TC_ue_cell_reselect_eutran_to_geran

Some later parts of the 4G->2G cell reselection where missing, mostly
related to tear down after MS has successfuly moved to the new SGSN. mainly:
- S6a Diameter Cancel Location Req + Answer initiated by HSS.
- Handle UeReleaseContextCommand and answer with
  UeReleaseContextComplete, emulating the ENB.
- handle S11 GTPv2C Delete Session Request and answer with Delete
  Session Response, emulating the SGW.

Change-Id: Ic0263a3aec922194aad22b031b2c82a99954354d
diff --git a/mme/MME_Tests.ttcn b/mme/MME_Tests.ttcn
index 3d081ba..339f121 100644
--- a/mme/MME_Tests.ttcn
+++ b/mme/MME_Tests.ttcn
@@ -178,6 +178,9 @@
 	/* S6 interface */
 	charstring mp_s6_local_ip := "127.0.0.4";
 	integer mp_s6_local_port := 3868;
+	charstring mp_s6_diam_realm := "localdomain";
+	charstring mp_s6_local_diam_host := "hss.localdomain";
+	charstring mp_s6_remote_diam_host := "mme.localdomain";
 
 	/* SGs interface */
 	charstring mp_sgs_local_ip := "127.0.0.1";
@@ -643,6 +646,30 @@
 	}
 }
 
+private altstep as_s1ap_handle_UeContextReleaseCmd(template S1AP_IEs.Cause cause := ?) runs on ConnHdlr {
+	var S1AP_PDU rx_msg;
+	var PDU_NAS_EPS rx_nas;
+	[] S1AP.receive(tr_S1AP_UeContextReleaseCmd(?, cause)) -> value rx_msg {
+		var template MME_UE_S1AP_ID mme_ue_id;
+		var template ENB_UE_S1AP_ID enb_ue_id;
+		if (not ispresent(rx_msg.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair)) {
+			/* TODO: The UE CONTEXT RELEASE COMMAND (see also: 3GPP TS 36.413, section 9.1.4.6), may identify the
+			* context by either an uE_S1AP_ID_pair (MME_UE_S1AP_ID and ENB_UE_S1AP_ID) or an MME_UE_S1AP_ID alone.
+			* The latter case is not implemented here yet. */
+			Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("complete implementation of UeContextReleaseCmd handling"));
+			return;
+		}
+
+		mme_ue_id := rx_msg.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair.mME_UE_S1AP_ID;
+		enb_ue_id := rx_msg.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair.eNB_UE_S1AP_ID;
+
+		S1AP.send(ts_S1AP_UeContextReleaseCompl(mme_ue_id, enb_ue_id));
+		}
+	[] S1AP.receive(PDU_NAS_EPS:?) -> value rx_nas {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Rx Unexpected NAS PDU msg: ", rx_nas));
+	}
+}
+
 /* Exepect AuthInfoReq (AIR) from HSS; respond with AuthInforAnswer (AIA) */
 private altstep as_DIA_AuthInfo() runs on ConnHdlr {
 	var PDU_DIAMETER rx_dia;
@@ -698,6 +725,33 @@
 	}
 }
 
+private function f_DIA_CancelLocation(integer idx := 0, template S1AP_IEs.Cause cause := omit) runs on ConnHdlr {
+
+	var UINT32 hbh_id := f_rnd_octstring(4);
+	var UINT32 ete_id := f_rnd_octstring(4);
+	var PDU_DIAMETER rx_dia;
+
+	/* Unlike CLR, CLA contains no IMSI. Register ete_id in DIAMETER_Emulation,
+	 * so AIA is forwarded back to us in DIAMETER port instead of MTC_CT.DIAMETER_UNIT.
+	 */
+	f_diameter_expect_eteid(ete_id);
+
+	DIAMETER.send(ts_DIA_CLR(g_pars.ue_pars.imsi, SGSN_UPDATE_PROCEDURE,
+		      orig_host := mp_s6_local_diam_host,
+		      orig_realm := mp_s6_diam_realm,
+		      dest_host := mp_s6_remote_diam_host,
+		      dest_realm := mp_s6_diam_realm,
+		      hbh_id := hbh_id,
+		      ete_id := ete_id));
+
+	alt {
+	[] DIAMETER.receive(tr_DIA_CLA) -> value rx_dia {}
+	[] DIAMETER.receive(PDU_DIAMETER:?) -> value rx_dia {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected Diameter S6b msg rx: ", rx_dia));
+		}
+	}
+}
+
 private altstep as_GTP2C_CreateSession_success() runs on ConnHdlr {
 	var PDU_GTPCv2 rx_msg;
 	var BearerContextIEs rx_bctx_ies;
@@ -781,6 +835,57 @@
 	}
 }
 
+private altstep as_GTP2C_DeleteSession_success() runs on ConnHdlr {
+	var PDU_GTPCv2 rx_msg;
+
+	[] GTP2.receive(tr_GTP2C_DeleteSessionReq(g_pars.ue_pars.s11_teic_local)) -> value rx_msg {
+		GTP2.send(ts_GTP2C_DeleteSessionResp(g_pars.ue_pars.s11_teic_remote,
+						     rx_msg.sequenceNumber,
+						     Request_accepted));
+		setverdict(pass);
+	}
+	[] GTP2.receive {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+			log2str("Unexpected GTPv2/S11 message from MME"));
+	}
+}
+
+private function f_gtp_sgsn_context_4g_to_2g(OCT4 new_sgsn_local_teid := '12345678'O) runs on ConnHdlr {
+	var template (value) GTPC_PDUs SGSNContextReqPDU;
+	var RoutingAreaIdentity rai;
+	var OCT4 ptmsi;
+	var OCT3 ptmsi_sig;
+	var Gtp1cUnitdata gtpc_pdu;
+	var OCT4 old_mme_local_teid;
+
+	guti2rai_ptmsi(g_pars.ue_pars.guti, rai, ptmsi, ptmsi_sig);
+
+	SGSNContextReqPDU := ts_SGSNContextReqPDU(rai, new_sgsn_local_teid, f_inet_addr(mp_gn_local_ip),
+						  ptmsi := ts_PTMSI(ptmsi), ptmsi_sig := ts_PTMSI_sig(ptmsi_sig));
+	GTP.send(ts_GTPC_SGSNContextReq(g_gn_iface_peer, 0, SGSNContextReqPDU));
+
+	timer T := 5.0;
+	T.start;
+	alt {
+	[] GTP.receive(tr_GTPC_SGSNContextResp(g_gn_iface_peer, new_sgsn_local_teid,
+					       tr_SGSNContextRespPDU(GTP_CAUSE_REQUEST_ACCEPTED,
+					       g_pars.ue_pars.imsi))) -> value gtpc_pdu {
+		old_mme_local_teid := gtpc_pdu.gtpc.gtpc_pdu.sgsn_ContextResponse.teidControlPlane.teidControlPlane;
+		setverdict(pass);
+		}
+	[] GTP.receive {
+		setverdict(fail, "unexpected GTPC message from MME");
+		}
+	[] T.timeout {
+		setverdict(fail, "no SGSN Context Response from MME");
+		}
+	}
+
+	GTP.send(ts_GTPC_SGSNContextAck(g_gn_iface_peer, old_mme_local_teid,
+					oct2int(gtpc_pdu.gtpc.opt_part.sequenceNumber),
+					ts_SGSNContextAckPDU(GTP_CAUSE_REQUEST_ACCEPTED)));
+
+}
 
 private function f_attach() runs on ConnHdlr {
 	var template (value) EPS_MobileIdentityV mi := ts_NAS_MobileId_IMSI(g_pars.ue_pars.imsi);
@@ -1231,47 +1336,26 @@
 	/* TODO: mMEC "also copied into the 8 Most Significant Bits of the NRI field within the P-TMSI" */
 }
 private function f_TC_ue_cell_reselect_eutran_to_geran(ConnHdlrPars pars) runs on ConnHdlr {
-	var template (value) GTPC_PDUs SGSNContextReqPDU;
-	var RoutingAreaIdentity rai;
-	var OCT4 ptmsi;
-	var OCT3 ptmsi_sig;
-	var Gtp1cUnitdata gtpc_pdu;
-	var OCT4 new_sgsn_local_teid := '12345678'O;
-	var OCT4 old_mme_local_teid;
-
 	f_init_handler(pars);
 	f_gtp_register_imsi(g_pars.ue_pars.imsi);
 	f_attach();
 
-	/* TODO: take GUTI from as_s1ap_handle_IntialCtxSetupReq() in g_pars,
-	 * convert it to P-TMSI and pass P-TMSI below */
-	guti2rai_ptmsi(g_pars.ue_pars.guti, rai, ptmsi, ptmsi_sig);
+	/* TS 23.401 Figure D.3.5-1 Steps 1,2,3,4: */
+	f_gtp_sgsn_context_4g_to_2g();
 
-	SGSNContextReqPDU := ts_SGSNContextReqPDU(rai, new_sgsn_local_teid, f_inet_addr(mp_gn_local_ip),
-						  ptmsi := ts_PTMSI(ptmsi), ptmsi_sig := ts_PTMSI_sig(ptmsi_sig));
-	GTP.send(ts_GTPC_SGSNContextReq(g_gn_iface_peer, 0, SGSNContextReqPDU));
+	/* TS 23.401 Figure D.3.5-1 Step 8: */
+	f_DIA_CancelLocation();
 
-	timer T := 5.0;
-	T.start;
-	alt {
-	[] GTP.receive(tr_GTPC_SGSNContextResp(g_gn_iface_peer, new_sgsn_local_teid,
-					       tr_SGSNContextRespPDU(GTP_CAUSE_REQUEST_ACCEPTED,
-					       g_pars.ue_pars.imsi))) -> value gtpc_pdu {
-		old_mme_local_teid := gtpc_pdu.gtpc.gtpc_pdu.sgsn_ContextResponse.teidControlPlane.teidControlPlane;
-		setverdict(pass);
-		}
-	[] GTP.receive {
-		setverdict(fail, "unexpected GTPC message from MME");
-		}
-	[] T.timeout {
-		setverdict(fail, "no SGSN Context Response from MME");
-		}
-	}
+	/* TS 23.401 Figure D.3.5-1 Step 13:
+	 * Upon rx of SGSN Context Acknowledge, MME released the ENB/UE context:
+	 */
+	as_s1ap_handle_UeContextReleaseCmd();
 
-	GTP.send(ts_GTPC_SGSNContextAck(g_gn_iface_peer, old_mme_local_teid,
-					oct2int(gtpc_pdu.gtpc.opt_part.sequenceNumber),
-					ts_SGSNContextAckPDU(GTP_CAUSE_REQUEST_ACCEPTED)));
-	/* Give some time to process the SGSNContextACK: */
+	/* TS 23.401 Figure D.3.5-1 Step 13:
+	 * After Gn timer triggers, the SGW session is deleted:
+	 */
+	as_GTP2C_DeleteSession_success();
+	/* Let MME some time to handle the Create Session Response: */
 	f_sleep(3.0);
 }
 testcase TC_ue_cell_reselect_eutran_to_geran() runs on MTC_CT {