mme: Implement missing UE attach steps

Related: OS#6294
Change-Id: Ib4a69aef40bf9dd17d640f650e459668f8415aaa
diff --git a/mme/MME_Tests.ttcn b/mme/MME_Tests.ttcn
index ce30279..eca6b04 100644
--- a/mme/MME_Tests.ttcn
+++ b/mme/MME_Tests.ttcn
@@ -13,6 +13,7 @@
 import from General_Types all;
 import from Native_Functions all;
 import from IPL4asp_Types all;
+import from Misc_Helpers all;
 import from S1AP_Types all;
 import from S1AP_Templates all;
 import from S1AP_Emulation all;
@@ -63,9 +64,34 @@
 	SupportedTAs supported_tas
 }
 
+type record BearerConfig {
+	 /* EPS Bearer ID */
+	uint4_t	ebi optional,
+	/* TEI (Data) local side, S11 (SGW) */
+	OCT4 	s11_teid_local optional,
+	/* TEI (Data) remote side, S11 (SGW) */
+	OCT4	s11_teid_remote optional,
+	/* TEI (Data) local side, S5c (PGW) */
+	OCT4 	s5c_teid_local optional,
+	/* TEI (Data) remote side, S5c (PGW) */
+	OCT4	s5c_teid_remote optional
+};
+
 /* parameters of emulated UE */
 type record UeParams {
-	hexstring imsi
+	hexstring imsi,
+	charstring ue_ip,
+
+	/* TEI (Control) local side, S11 (SGW) */
+	OCT4 	s11_teic_local,
+	/* TEI (Control) remote side, S11 (SGW) */
+	OCT4	s11_teic_remote optional,
+	/* TEI (Control) local side, S5c (PGW) */
+	OCT4 	s5c_teic_local,
+	/* TEI (Control) remote side, S5c (PGW) */
+	OCT4	s5c_teic_remote optional,
+
+	BearerConfig bearer optional
 }
 
 type component MTC_CT {
@@ -168,6 +194,9 @@
 	integer mp_s11_local_port := 2123;
 	charstring mp_s11_remote_ip := "127.0.0.2";
 	integer mp_s11_remote_port := 2123;
+
+	/* PGW information announced by SGWC. MME never really interacts with these. */
+	charstring mp_s5c_pgw_ip := "1.2.3.4";
 }
 
 /* send incoming unit data messages (like reset) to global SGsAP_UNIT port */
@@ -246,7 +275,19 @@
 }
 friend function f_init_one_ue(inout UeParams uep, integer imsi_suffix) {
 	uep := {
-		imsi := f_gen_imsi(imsi_suffix)
+		imsi := f_gen_imsi(imsi_suffix),
+		ue_ip := "192.168.123.50",
+		s11_teic_local := '00000000'O,
+		s11_teic_remote := omit,
+		s5c_teic_local := '00000000'O,
+		s5c_teic_remote := omit,
+		bearer := {
+			ebi := omit,
+			s11_teid_local := omit,
+			s11_teid_remote := omit,
+			s5c_teid_local := omit,
+			s5c_teid_remote := omit
+		}
 	}
 }
 friend function f_init_s1ap(charstring id, integer imsi_suffix) runs on MTC_CT {
@@ -561,6 +602,33 @@
 		}
 }
 
+
+private altstep as_s1ap_handle_IntialCtxSetupReq() runs on ConnHdlr {
+	var S1AP_PDU rx_msg;
+	var PDU_NAS_EPS rx_nas;
+	[] S1AP.receive(tr_S1AP_IntialCtxSetupReq) -> value rx_msg {
+		var template (omit) MME_UE_S1AP_ID mme_ue_id := f_S1AP_get_MME_UE_S1AP_ID(rx_msg);
+		var template (omit) ENB_UE_S1AP_ID enb_ue_id := f_S1AP_get_ENB_UE_S1AP_ID(rx_msg);
+		var template (value) E_RABSetupItemCtxtSURes rab_setup_it;
+		var template (value) E_RABSetupListCtxtSURes rab_setup_items;
+		var octetstring esm_enc;
+		var template (value) PDU_NAS_EPS nas;
+
+		rab_setup_it := ts_S1AP_RABSetupItemCtxtSURes(rab_id := 5,
+							      tla := oct2bit(f_inet_addr(mp_mme_ip)),
+							      gtp_teid := '00000002'O);
+		rab_setup_items := ts_S1AP_RABSetupListCtxtSURes(rab_setup_it);
+		S1AP.send(ts_S1AP_InitialCtxSetupResp(valueof(mme_ue_id), valueof(enb_ue_id), rab_setup_items));
+
+		nas := ts_NAS_ActDefEpsBearCtxAck(int2bit(g_pars.ue_pars.bearer.ebi, 4), '00000000'B, omit);
+		esm_enc := enc_PDU_NAS_EPS(valueof(nas));
+		S1AP.send(ts_NAS_AttachComplete(esm_enc));
+		}
+	[] 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;
@@ -616,18 +684,95 @@
 	}
 }
 
-private function f_TC_attach(ConnHdlrPars pars) runs on ConnHdlr {
+private altstep as_GTP2C_CreateSession_success() runs on ConnHdlr {
+	var PDU_GTPCv2 rx_msg;
+	var BearerContextIEs rx_bctx_ies;
+	var template (value) FullyQualifiedTEID s11_fteid_c_ie, s11_fteid_u_ie, s5c_fteid_c_ie, s5c_fteid_u_ie;
+	var template (value) PDN_AddressAllocation paa;
+	var template (value) BearerContextIEs bctx_ies;
 
-	f_init_handler(pars);
-	var template (value) EPS_MobileIdentityV mi := ts_NAS_MobileId_IMSI(pars.ue_pars.imsi);
+	[] GTP2.receive(tr_GTP2C_CreateSessionReq(g_pars.ue_pars.imsi)) -> value rx_msg {
+		/* Parse TEIC and Bearer EBI and TEID and store it in g_pars */
+		g_pars.ue_pars.s11_teic_remote := rx_msg.gtpcv2_pdu.createSessionRequest.fullyQualifiedTEID[0].tEID_GRE_Key;
+		g_pars.ue_pars.s5c_teic_remote := rx_msg.gtpcv2_pdu.createSessionRequest.fullyQualifiedTEID[1].tEID_GRE_Key;
+
+		rx_bctx_ies := rx_msg.gtpcv2_pdu.createSessionRequest.bearerContextGrouped[0].bearerContextIEs;
+		g_pars.ue_pars.bearer.ebi := rx_bctx_ies.ePS_Bearer_ID.ePS_Bearer_ID_Value;
+
+		/* allocate + register TEID-C on local side */
+		g_pars.ue_pars.s11_teic_local := f_gtp2_allocate_teid();
+		g_pars.ue_pars.bearer.s11_teid_local := g_pars.ue_pars.s11_teic_local;
+		g_pars.ue_pars.s5c_teic_local := f_gtp2_allocate_teid();
+		g_pars.ue_pars.bearer.s5c_teid_local := g_pars.ue_pars.s5c_teic_local;
+
+		s11_fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S11_MME_GTPC, g_pars.ue_pars.s11_teic_local, 0,
+					f_inet_addr(mp_s11_local_ip), omit);
+		s5c_fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_PGW_GTPC, g_pars.ue_pars.s5c_teic_local, 1,
+					f_inet_addr(mp_s5c_pgw_ip), omit);
+		s11_fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S1U_SGW_GTPU, g_pars.ue_pars.bearer.s11_teid_local, 0,
+					f_inet_addr(mp_s11_local_ip), omit);
+		s5c_fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_PGW_GTPU, g_pars.ue_pars.bearer.s5c_teid_local, 2,
+					f_inet_addr(mp_s5c_pgw_ip), omit);
+		paa := ts_GTP2C_PdnAddrAlloc_v4(f_inet_addr(g_pars.ue_pars.ue_ip));
+		bctx_ies := ts_GTP2C_BcContextIE(ebi := g_pars.ue_pars.bearer.ebi,
+						 teid_list := { s11_fteid_u_ie, s5c_fteid_u_ie },
+						 qos := ts_GTP2C_BearerQos('09'O, 0, 0, 0, 0),
+						 charging_id := ts_GTP2C_ChargingID(g_pars.ue_pars.bearer.s11_teid_local));
+
+		GTP2.send(ts_GTP2C_CreateSessionResp(g_pars.ue_pars.s11_teic_remote,
+						     rx_msg.sequenceNumber,
+						     { s11_fteid_c_ie, s5c_fteid_c_ie },
+						     paa, { ts_GTP2C_BcGrouped(bctx_ies) } ));
+		setverdict(pass);
+	}
+	[] GTP2.receive {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+			log2str("Unexpected GTPv2/S11 message from MME"));
+	}
+}
+
+private altstep as_GTP2C_ModifyBearer_success() runs on ConnHdlr {
+	var PDU_GTPCv2 rx_msg;
+	var BearerContextIEs rx_bctx_ies;
+	var template (value) FullyQualifiedTEID s11_fteid_c_ie, s11_fteid_u_ie, s5c_fteid_c_ie, s5c_fteid_u_ie;
+	var template (value) BearerContextIEs bctx_ies;
+
+	[] GTP2.receive(tr_GTP2C_ModifyBearerReq(g_pars.ue_pars.s11_teic_local)) -> value rx_msg {
+
+		rx_bctx_ies := rx_msg.gtpcv2_pdu.modifyBearerRequest.bearerContextGrouped[0].bearerContextIEs;
+
+		/* TODO: validate the S1-U fullyQualifiedTEID announces the IP address provided by the ENB in InitialCtxSetupResp */
+		// rx_bctx_ies.fullyQualifiedTEID[0]. == f_inet_addr(mp_mme_ip)
+
+		/* Update S11 TEID */
+		g_pars.ue_pars.bearer.s11_teid_remote := rx_bctx_ies.fullyQualifiedTEID[0].tEID_GRE_Key;
+
+		s11_fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S1U_SGW_GTPU, g_pars.ue_pars.bearer.s11_teid_local, 0,
+					f_inet_addr(mp_s11_local_ip), omit);
+		bctx_ies := ts_GTP2C_BcContextIE(ebi := g_pars.ue_pars.bearer.ebi,
+						 teid_list := { s11_fteid_u_ie },
+						 qos := ts_GTP2C_BearerQos('09'O, 0, 0, 0, 0),
+						 charging_id := ts_GTP2C_ChargingID(g_pars.ue_pars.bearer.s11_teid_local));
+
+		GTP2.send(ts_GTP2C_ModifyBearerResp(g_pars.ue_pars.s11_teic_remote,
+						     rx_msg.sequenceNumber,
+						     Request_accepted,
+						     g_pars.ue_pars.bearer.ebi,
+						     { ts_GTP2C_BcGrouped(bctx_ies) } ));
+		setverdict(pass);
+	}
+	[] GTP2.receive {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+			log2str("Unexpected GTPv2/S11 message from MME"));
+	}
+}
+
+
+private function f_attach() runs on ConnHdlr {
+	var template (value) EPS_MobileIdentityV mi := ts_NAS_MobileId_IMSI(g_pars.ue_pars.imsi);
 	var template (value) PDU_NAS_EPS nas_esm, nas_emm;
 	timer T := 5.0;
 
-/*
-	nas_esm := ts_NAS_ActDefEpsBearCtxReq(bearer_id := '0000'B, proc_tid := int2bit(1,8),
-					      qos := c_NAS_defaultQoS, apn := c_NAS_defaultAPN,
-					      addr_type := '000'B, addr_info := ''O);
-*/
 	nas_esm := ts_NAS_PdnConnReq(bearer_id := '0000'B, proc_tid := int2bit(1,8),
 					pdn_type := NAS_PDN_T_IPv4, req_type := '001'B);
 	nas_emm := ts_NAS_AttachRequest(att_type := '000'B, kset_id := '000'B, mobile_id := mi,
@@ -652,23 +797,31 @@
 	}
 
 	/* We now expect the MME to send a Create Session Request to the SGW-C */
-	/* TODO: be more restrictive, fix and use tr_GTP2C_CreateSessionReq */
 	f_gtp2_register_udmsg('20'O);
 	T.start;
 	alt {
-	[] GTP2.receive(tr_PDU_GTP2C) {
-		setverdict(pass);
-		}
-	[] GTP2.receive {
-		setverdict(fail, "unexpected GTPv2/S11 message from MME");
-		}
-	[] T.timeout {
-		setverdict(fail, "no message from MME");
-		}
+	[] as_GTP2C_CreateSession_success();
+	[] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("No message from MME")); }
 	}
 
-	/* TODO: Finish this procedure until Attach Complete */
+	T.start;
+	alt {
+	[] as_s1ap_handle_IntialCtxSetupReq();
+	[] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("No message from MME")); }
+	}
 
+	/* We now expect the MME to send a Modify Bearer Request to the SGW-C */
+	f_gtp2_register_udmsg('22'O);
+	T.start;
+	alt {
+	[] as_GTP2C_ModifyBearer_success();
+	[] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("No message from MME")); }
+	}
+}
+
+private function f_TC_attach(ConnHdlrPars pars) runs on ConnHdlr {
+	f_init_handler(pars);
+	f_attach();
 }
 testcase TC_s1ap_attach() runs on MTC_CT {
 	var charstring id := testcasename();