pespin has submitted this change. (
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/41084?usp=email )
Change subject: mme: Store and check {mme,enb}_ue_id over S1AP
......................................................................
mme: Store and check {mme,enb}_ue_id over S1AP
* Generate a different enb_ue_id based on imsi_suffix, and store it
during startup of ConnHdlr.
* Validate the MME sends the expected message to the expected enb_ue_id.
* Store the mme_ue_id and validate also that MME keeps using it.
A new procedure is added to S1AP_Emulation, similar to what already
exists in NGAP_Emulation, to obtain the mme_ue_id allocated by the peer.
This is needed because upon rx of DL NAS Transport messages we only
receive the upper layer NAS decoded in the Connhdlr, so we can't store
and track the MME ID early enough.
Change-Id: I62902db3851f48ce9f80cb1cc84797735c0091de
---
M library/S1AP_Emulation.ttcn
M library/s1ap/S1AP_Templates.ttcn
M mme/ConnHdlr.ttcn
M mme/MME_Tests.ttcn
4 files changed, 138 insertions(+), 33 deletions(-)
Approvals:
osmith: Looks good to me, but someone else must approve
pespin: Looks good to me, approved
laforge: Looks good to me, but someone else must approve
Jenkins Builder: Verified
diff --git a/library/S1AP_Emulation.ttcn b/library/S1AP_Emulation.ttcn
index b3e7731..9132ef3 100644
--- a/library/S1AP_Emulation.ttcn
+++ b/library/S1AP_Emulation.ttcn
@@ -526,6 +526,11 @@
f_create_expect_proc(procedureCode, vc_conn);
S1AP_PROC.reply(S1APEM_register_proc:{procedureCode, vc_conn}) to vc_conn;
}
+ [] S1AP_PROC.getcall(S1APEM_obtain_mme_ue_id:{}) -> sender vc_conn {
+ var integer i := f_assoc_id_by_comp(vc_conn);
+ mme_id := S1apAssociationTable[i].mme_ue_s1ap_id;
+ S1AP_PROC.reply(S1APEM_obtain_mme_ue_id:{} value mme_id) to vc_conn;
+ }
[] S1AP_PROC.getcall(S1APEM_derive_nas_token:{?, ?, -}) -> param(kasme, vc_conn) {
var integer assoc_id := f_assoc_id_by_comp(vc_conn);
var OCT32 nas_token := f_kdf_nas_token(kasme,
S1apAssociationTable[assoc_id].nus.tx_count)
@@ -553,11 +558,13 @@
signature S1APEM_register(in MME_UE_S1AP_ID mme_id, in ENB_UE_S1AP_ID enb_id, in
S1AP_ConnHdlr hdlr);
signature S1APEM_register_proc(in integer procedureCode, in S1AP_ConnHdlr hdlr);
+signature S1APEM_obtain_mme_ue_id() return MME_UE_S1AP_ID;
signature S1APEM_derive_nas_token(in octetstring kasme, in S1AP_ConnHdlr hdlr, out OCT32
nas_token);
type port S1APEM_PROC_PT procedure {
inout S1APEM_register;
inout S1APEM_register_proc;
+ inout S1APEM_obtain_mme_ue_id;
inout S1APEM_derive_nas_token;
} with { extension "internal" };
@@ -658,6 +665,19 @@
log(procedureCode);
}
+/* ConnHdlr function to obtain MME_UD_ID internally stored in association table.
+ * This is needed because we don't receive S1AP layer of DownlinkNasTransport in
+ * ConnHdlr from Emulation, so there's no direct way to obtain it early during
+ * registration procedure. */
+function f_s1apem_obtain_mme_ue_id() runs on S1AP_ConnHdlr return MME_UE_S1AP_ID {
+ var MME_UE_S1AP_ID mme_ue_id;
+ S1AP_PROC.call(S1APEM_obtain_mme_ue_id:{}) {
+ [] S1AP_PROC.getreply(S1APEM_obtain_mme_ue_id:{}) -> value mme_ue_id {
+ return mme_ue_id;
+ }
+ }
+}
+
/* Derive NAS Token (and post-increment ul_count): */
function f_s1apem_derive_nas_token(in octetstring kasme) runs on S1AP_ConnHdlr return
OCT32
{
diff --git a/library/s1ap/S1AP_Templates.ttcn b/library/s1ap/S1AP_Templates.ttcn
index 8c89c75..8151982 100644
--- a/library/s1ap/S1AP_Templates.ttcn
+++ b/library/s1ap/S1AP_Templates.ttcn
@@ -3046,4 +3046,42 @@
iE_Extensions := *
};
+/* 9.2.3.18 UE S1AP pair */
+template (value) UE_S1AP_ID_pair
+ts_S1AP_UE_ID_pair(template (value) MME_UE_S1AP_ID mme_ue_id,
+ template (value) ENB_UE_S1AP_ID enb_ue_id) := {
+ mME_UE_S1AP_ID := mme_ue_id,
+ eNB_UE_S1AP_ID := enb_ue_id,
+ iE_Extensions := omit
+};
+
+template (present) UE_S1AP_ID_pair
+tr_S1AP_UE_ID_pair(template (present) MME_UE_S1AP_ID mme_ue_id := ?,
+ template (present) ENB_UE_S1AP_ID enb_ue_id := ?) := {
+ mME_UE_S1AP_ID := mme_ue_id,
+ eNB_UE_S1AP_ID := enb_ue_id,
+ iE_Extensions := *
+};
+
+/* CHOICE UE S1AP IDs */
+template (value) UE_S1AP_IDs
+ts_S1AP_UE_IDs_pair(template (value) MME_UE_S1AP_ID mme_ue_id,
+ template (value) ENB_UE_S1AP_ID enb_ue_id) := {
+ uE_S1AP_ID_pair := ts_S1AP_UE_ID_pair(mme_ue_id, enb_ue_id)
+};
+template (present) UE_S1AP_IDs
+tr_S1AP_UE_IDs_pair(template (present) MME_UE_S1AP_ID mme_ue_id := ?,
+ template (present) ENB_UE_S1AP_ID enb_ue_id := ?) := {
+ uE_S1AP_ID_pair := tr_S1AP_UE_ID_pair(mme_ue_id, enb_ue_id)
+};
+
+template (value) UE_S1AP_IDs
+ts_S1AP_UE_IDs_mme(template (value) MME_UE_S1AP_ID mme_ue_id) := {
+ mME_UE_S1AP_ID := mme_ue_id
+};
+template (present) UE_S1AP_IDs
+tr_S1AP_UE_IDs_mme(template (present) MME_UE_S1AP_ID mme_ue_id := ?) := {
+ mME_UE_S1AP_ID := mme_ue_id
+};
+
}
diff --git a/mme/ConnHdlr.ttcn b/mme/ConnHdlr.ttcn
index 8f4d972..4e94cbf 100644
--- a/mme/ConnHdlr.ttcn
+++ b/mme/ConnHdlr.ttcn
@@ -108,6 +108,9 @@
NAS_EPS_Types.GUTI guti optional,
octetstring kasme optional,
+ ENB_UE_S1AP_ID enb_ue_id,
+ MME_UE_S1AP_ID mme_ue_id optional,
+
/* TEI (Control) local side, S11 (SGW) */
OCT4 s11_teic_local,
/* TEI (Control) remote side, S11 (SGW) */
@@ -297,9 +300,8 @@
private altstep as_s1ap_handle_IntialCtxSetupReq_Attach_Accept() 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);
+ [] S1AP.receive(tr_S1AP_IntialCtxSetupReq(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.enb_ue_id)) -> value rx_msg {
var template (value) E_RABSetupItemCtxtSURes rab_setup_it;
var template (value) E_RABSetupListCtxtSURes rab_setup_items;
var octetstring esm_enc;
@@ -317,7 +319,9 @@
tla :=
oct2bit(f_inet_addr(g_pars.enb_pars[g_pars.mme_idx].gtp1u_local_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));
+ S1AP.send(ts_S1AP_InitialCtxSetupResp(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.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));
@@ -334,14 +338,18 @@
altstep as_s1ap_handle_IntialCtxSetupReq_TAU_Accept() runs on ConnHdlr {
var S1AP_PDU rx_msg;
var PDU_NAS_EPS rx_nas;
- [] S1AP.receive(tr_S1AP_IntialCtxSetupReq) -> value rx_msg {
+ [] S1AP.receive(tr_S1AP_IntialCtxSetupReq(f_tr_s1ap_mme_ue_id(),
+ g_pars.ue_pars.enb_ue_id)) -> value rx_msg {
/* 3GPP TS 23.401 D.3.6 step 22: */
- 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 S1APEM_Config cfg;
+ if (not ispresent(g_pars.ue_pars.mme_ue_id)) {
+ /* After receiving IntialCtxSetupReq above, the MME-UE-Id is know, update it: */
+ g_pars.ue_pars.mme_ue_id := valueof(f_S1AP_get_MME_UE_S1AP_ID(rx_msg));
+ }
+
S1AP.receive(tr_PDU_NAS_EPS_TrackingAreaUpdateAccept)-> value rx_nas;
/* Configure integrity protection: */
@@ -354,7 +362,9 @@
tla := oct2bit(f_inet_addr(g_pars.enb_pars[g_pars.mme_idx].gtp1u_local_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));
+ S1AP.send(ts_S1AP_InitialCtxSetupResp(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.enb_ue_id,
+ rab_setup_items));
/* 3GPP TS 23.401 D.3.6 step 23: */
/* Integrity Protection and Ciphering implemented by S1AP_Emulation: */
@@ -378,41 +388,70 @@
altstep as_s1ap_handle_UeContextModificationReq(template CSFallbackIndicator csfb_ind :=
omit) runs on ConnHdlr {
var S1AP_PDU rx_msg;
var PDU_NAS_EPS rx_nas;
- [] S1AP.receive(tr_S1AP_UeContextModificationReq(?, ?,
+ [] S1AP.receive(tr_S1AP_UeContextModificationReq(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.enb_ue_id,
csfb_ind := cs_fallback_required,
registered_lai := f_SGsAP_LAI_to_S1AP_LAI(g_pars.sgsap_pars.lai))) -> value
rx_msg {
- var template MME_UE_S1AP_ID mme_ue_id;
- var template ENB_UE_S1AP_ID enb_ue_id;
-
- mme_ue_id := f_S1AP_get_MME_UE_S1AP_ID(rx_msg);
- enb_ue_id := f_S1AP_get_ENB_UE_S1AP_ID(rx_msg);
-
- S1AP.send(ts_S1AP_UeContextModificationResp(mme_ue_id, enb_ue_id));
+ S1AP.send(ts_S1AP_UeContextModificationResp(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.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));
}
}
+/* Expect a given mme_ue_id for ConnHdlr if already known */
+private function f_tr_s1ap_mme_ue_id()
+runs on ConnHdlr return template (present) MME_UE_S1AP_ID
+{
+ var template (present) MME_UE_S1AP_ID tpl;
+ if (ispresent(g_pars.ue_pars.mme_ue_id)) {
+ tpl := g_pars.ue_pars.mme_ue_id;
+ } else {
+ tpl := ?
+ }
+ return tpl;
+}
+
+/* 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. */
+private function f_tr_s1ap_UeContextReleaseCmd_ue_ids()
+runs on ConnHdlr return template (present) UE_S1AP_IDs
+{
+ var template (present) UE_S1AP_IDs tpl;
+ /* If we receive a NAS payload with a reject to our InitialUE message
+ * followed by a UEContextReleaseCommand, we have no time to actually
+ * obtain the mme_ue_id from the S1AP_Emulation since it gets freed during
+ * rx of UECtxReleaseCmd.
+ * Under that scenario, allow any mme_ue_id */
+ if (ispresent(g_pars.ue_pars.mme_ue_id)) {
+ tpl := (
+ tr_S1AP_UE_IDs_pair(g_pars.ue_pars.mme_ue_id,
+ g_pars.ue_pars.enb_ue_id),
+ tr_S1AP_UE_IDs_mme(g_pars.ue_pars.mme_ue_id)
+ );
+ } else {
+ tpl := (
+ tr_S1AP_UE_IDs_pair(?,
+ g_pars.ue_pars.enb_ue_id),
+ tr_S1AP_UE_IDs_mme(?)
+ );
+ }
+ return tpl;
+}
+
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(tr_S1AP_UeContextReleaseCmd(f_tr_s1ap_UeContextReleaseCmd_ue_ids(),
+ cause)) -> value rx_msg {
+ S1AP.send(ts_S1AP_UeContextReleaseCompl(f_S1AP_get_MME_UE_S1AP_ID(rx_msg),
+ g_pars.ue_pars.enb_ue_id));
}
+ [] S1AP.receive(S1AP_PDU:?) -> value rx_msg {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Rx Unexpected S1AP PDU
msg: ", rx_msg));
+ }
[] S1AP.receive(PDU_NAS_EPS:?) -> value rx_nas {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Rx Unexpected NAS PDU
msg: ", rx_nas));
}
@@ -741,7 +780,8 @@
ue_net_cap := c_NAS_defaultUeNetCap,
esm_enc := enc_PDU_NAS_EPS(valueof(nas_esm)));
var template (value) S1AP_PDU tx;
- tx := ts_S1AP_InitialUE(p_eNB_value := 0, p_nasPdu :=
enc_PDU_NAS_EPS(valueof(nas_emm)),
+ tx := ts_S1AP_InitialUE(p_eNB_value := g_pars.ue_pars.enb_ue_id,
+ p_nasPdu := enc_PDU_NAS_EPS(valueof(nas_emm)),
p_tAI := ts_enb_S1AP_TAI(g_pars.enb_pars[g_pars.mme_idx]),
p_eUTRAN_CGI := ts_enb_S1AP_CGI(g_pars.enb_pars[g_pars.mme_idx]),
p_rrcCause := mo_Signalling);
@@ -768,6 +808,9 @@
}
}
+ /* After receiving DL NAS Transport above, the MME-UE-Id became known in S1AP_Emulation:
*/
+ g_pars.ue_pars.mme_ue_id := f_s1apem_obtain_mme_ue_id();
+
/* We now expect the MME to send a Create Session Request to the SGW-C */
f_gtp2_register_udmsg('20'O);
T.start;
diff --git a/mme/MME_Tests.ttcn b/mme/MME_Tests.ttcn
index 834c411..d4cecdc 100644
--- a/mme/MME_Tests.ttcn
+++ b/mme/MME_Tests.ttcn
@@ -212,6 +212,8 @@
ue_ip := "192.168.123.50",
guti := omit,
kasme := omit,
+ enb_ue_id := imsi_suffix,
+ mme_ue_id := omit,
s11_teic_local := '00000000'O,
s11_teic_remote := omit,
s5c_teic_local := '00000000'O,
@@ -610,7 +612,8 @@
var EPS_MobileIdentityLV old_guti := valueof(ts_EPS_MobileId_GUTI(mcc_mnc,
'0001'O, '01'O, 'AABBCCDD'O));
nas_tau := ts_PDU_NAS_EPS_TrackingAreaUpdateRequest(old_guti, g_pars.kset_id);
- tx := ts_S1AP_InitialUE(p_eNB_value := 0, p_nasPdu :=
enc_PDU_NAS_EPS(valueof(nas_tau)),
+ tx := ts_S1AP_InitialUE(p_eNB_value := g_pars.ue_pars.enb_ue_id,
+ p_nasPdu := enc_PDU_NAS_EPS(valueof(nas_tau)),
p_tAI := ts_enb_S1AP_TAI(g_pars.enb_pars[g_pars.mme_idx]),
p_eUTRAN_CGI := ts_enb_S1AP_CGI(g_pars.enb_pars[g_pars.mme_idx]),
p_rrcCause := mo_Signalling);
@@ -718,7 +721,8 @@
ts_NonceTV('12345678'O),
ts_CipheringKeySequenceNumberTV('000'B),
ue_net_cap := ue_net_cap);
- tx := ts_S1AP_InitialUE(p_eNB_value := 0, p_nasPdu :=
enc_PDU_NAS_EPS(valueof(nas_tau)),
+ tx := ts_S1AP_InitialUE(p_eNB_value := g_pars.ue_pars.enb_ue_id,
+ p_nasPdu := enc_PDU_NAS_EPS(valueof(nas_tau)),
p_tAI := ts_enb_S1AP_TAI(g_pars.enb_pars[g_pars.mme_idx]),
p_eUTRAN_CGI := ts_enb_S1AP_CGI(g_pars.enb_pars[g_pars.mme_idx]),
p_rrcCause := mo_Signalling);
--
To view, visit
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/41084?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I62902db3851f48ce9f80cb1cc84797735c0091de
Gerrit-Change-Number: 41084
Gerrit-PatchSet: 4
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>