mme: Implement missing UE attach steps

Related: OS#6294
Change-Id: Ib4a69aef40bf9dd17d640f650e459668f8415aaa
diff --git a/library/GTPv2_Templates.ttcn b/library/GTPv2_Templates.ttcn
index 1ccbcb8..c09bca8 100644
--- a/library/GTPv2_Templates.ttcn
+++ b/library/GTPv2_Templates.ttcn
@@ -1040,6 +1040,65 @@
 		privateExtension := omit
 	}});
 
+template (present) PDU_GTPCv2
+tr_GTP2C_ModifyBearerReq(template (present) OCT4 d_teid := ?,
+			  template (present) OCT3 seq := ?) :=
+tr_PDU_GTP2C(d_teid, seq, {
+	modifyBearerRequest := {
+		mEI := *,
+		userLocationInfo := *,
+		servingNetwork := *,
+		rAT_Type := *,
+		indicationFlags := *,
+		fullyQualifiedTEID := *,
+		ambr := *,
+		delayDownlinkPacketNotificationReq := *,
+		bearerContextGrouped := *,
+		recovery := *,
+		uE_TimeZone := *,
+		csid := *,
+		user_CSG_Information := *,
+		iP_Addr := *,
+		portNumber := *,
+		lDN := *,
+		//maxMBR_APN_AMBR := *,
+		cNOperatorSelectionEntity := *,
+		presenceReportingAreaInformation := *,
+		overloadControlInformationGrouped := *,
+		servingPLMNRateControl := *,
+		counter := *,
+		privateExtension := *
+	}});
+
+template (value) PDU_GTPCv2
+ts_GTP2C_ModifyBearerResp(template (value) OCT4 d_teid,
+			  template (value) OCT3 seq,
+			  template (value) GTP2C_Cause cause,
+			  template (value) uint4_t bearer_id,
+			  template (omit) BearerContextGrouped_List bearerContextGrouped := omit) :=
+ts_PDU_GTP2C(d_teid, seq, '23'O, {
+	modifyBearerResponse := {
+		cause := ts_GTP2C_Cause(cause, '0'B),
+		mSISDN := omit,
+		linkedEPS_Bearer_ID := ts_GTP2C_EpsBearerId(bearer_id),
+		aPN_Restriction := omit,
+		protocolConfigOptions := omit,
+		bearerContextGrouped := bearerContextGrouped,
+		changeReportingAction := omit,
+		cSG_InformationReportingAction := omit,
+		heNBInformationReporting := omit,
+		chargingGatewayName := omit,
+		chargingGatewayAddress := omit,
+		csid := omit,
+		recovery := omit,
+		lDN := omit,
+		indicationFlags := omit,
+		presenceReportingAreaAction := omit,
+		loadControlInformationGrouped := omit,
+		overloadControlInformationGrouped := omit,
+		pDNConnectionChargingID := omit,
+		privateExtension := omit
+	}});
 
 template (value) PDU_GTPCv2
 ts_GTP2C_DeleteBearerReq(template (value) OCT4 d_teid,
diff --git a/library/NAS_Templates.ttcn b/library/NAS_Templates.ttcn
index 45f5f8d..70561b3 100644
--- a/library/NAS_Templates.ttcn
+++ b/library/NAS_Templates.ttcn
@@ -1025,6 +1025,55 @@
 		}
 	}
 }
+template (present) PDU_NAS_EPS
+tr_NAS_ActDefEpsBearCtxReq(template (present) BIT4 bearer_id := ?,
+			   template (present) BIT8 proc_tid := ?,
+			   template (present) EPS_QualityOfServiceV qos := ?,
+			   template (present) octetstring apn := ?,
+			   template (present) BIT3 addr_type := ?,
+			   template (present) octetstring addr_info := ?) := {
+	protocolDiscriminator := c_EPS_NAS_PD_ESM,
+	ePS_messages := {
+		ePS_SessionManagement := {
+			pDU_NAS_EPS_ActDefEPSBearerContextRequest := {
+				ePSBearerIdentity := bearer_id,
+				procedureTransactionIdentifier := proc_tid,
+				messageType := '11000001'B,
+				ePS_QoS := {
+					lengthIndicator := ?,
+					ePS_QualityOfServiceV := qos
+				},
+				accessPointName := {
+					lengthIndicator := 0,
+					accessPointNameValue := apn
+				},
+				pDN_Address := {
+					lengthIndicator := 0,
+					typeValue := addr_type,
+					spare := '00000'B,
+					addressInformation := addr_info
+				},
+				transactionIdentifier := *,
+				negotiatedQoS := *,
+				negotiated_LLC_SAPI := *,
+				radioPriority := *,
+				packetFlowID := *,
+				aPN_AMBR := *,
+				esmCause := *,
+				protocolConfigOptions := *,
+				connectivityType := *,
+				wLANOffloadIndication := *,
+				nBIFOMContainer := *,
+				headerCompressinConfiguration := *,
+				controlPlaneOnlyIndication := *,
+				extendedProtocolConfigurationOptions := *,
+				servingPLMNRateControl := *,
+				extended_APN_AMBR := *,
+				extendedQoS := *
+			}
+		}
+	}
+}
 
 /* 8.3.4 Activate Default EPS Bearer Context Accept */
 template (value) PDU_NAS_EPS
diff --git a/library/s1ap/S1AP_Templates.ttcn b/library/s1ap/S1AP_Templates.ttcn
index 9e75cec..26e1ae1 100644
--- a/library/s1ap/S1AP_Templates.ttcn
+++ b/library/s1ap/S1AP_Templates.ttcn
@@ -22,7 +22,7 @@
  *********************************************************************************/
 
 /*********************************************************************************
- * 9.1.8 Management Messages 
+ * 9.1.8 Management Messages
  *********************************************************************************/
 
 /* 9.1.8.4 S1 SETUP REQUEST */
@@ -578,11 +578,30 @@
 }
 
 /* 9.1.4.3 INITIAL CONTEXT SETUP RESPONSE */
+template (value) E_RABSetupItemCtxtSURes
+ts_S1AP_RABSetupItemCtxtSURes(template (value) E_RAB_ID rab_id := 5,
+			      template (value) TransportLayerAddress tla := '00001010000101110001100000000100'B,
+			      template (value) GTP_TEID gtp_teid := '00000002'O) := {
+					e_RAB_ID := rab_id,
+					transportLayerAddress := tla,
+					gTP_TEID := gtp_teid,
+					iE_Extensions := omit
+}
+template (value) E_RABSetupListCtxtSURes
+ts_S1AP_RABSetupListCtxtSURes(template (value) E_RABSetupItemCtxtSURes it := ts_S1AP_RABSetupItemCtxtSURes()) := {
+	{
+		id := S1AP_Constants.id_E_RABSetupItemCtxtSURes,
+		criticality := ignore,
+		value_ := { E_RABSetupItemCtxtSURes := it }
+	}
+}
+
 template (value) S1AP_PDU
 ts_S1AP_InitialCtxSetupResp(template (value) MME_UE_S1AP_ID mme_id,
 			    template (value) ENB_UE_S1AP_ID enb_id,
-			    template (value) E_RABSetupListCtxtSURes rab_setup_items,
-			    template (value) E_RABList rab_items) := {
+			    template (value) E_RABSetupListCtxtSURes rab_setup_items
+			    /*OPTIONAL: template (value) E_RABList rab_items */
+			    ) := {
 	successfulOutcome := {
 		procedureCode := id_InitialContextSetup,
 		criticality := reject,
@@ -598,14 +617,15 @@
 						criticality := reject,
 						value_ := {ENB_UE_S1AP_ID := enb_id}
 					}, {
-						id := S1AP_Constants.id_E_RABSetupListBearerSURes,
+						id := S1AP_Constants.id_E_RABSetupListCtxtSURes,
 						criticality := ignore,
 						value_ := {E_RABSetupListCtxtSURes := rab_setup_items}
-					}, {
+					}
+					/*, {
 						id := S1AP_Constants.id_E_RABFailedToSetupListBearerSURes,
 						criticality := ignore,
 						value_ := {E_RABList := rab_items}
-					}
+					}*/
 				}
 			}
 		}
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();