library: Add S1AP CodecPort/Emulation
Change-Id: I9bfba3ab2a3830e590b203c44c03b9c9383fff99
diff --git a/library/S1AP_Emulation.ttcn b/library/S1AP_Emulation.ttcn
new file mode 100644
index 0000000..d09b499
--- /dev/null
+++ b/library/S1AP_Emulation.ttcn
@@ -0,0 +1,681 @@
+module S1AP_Emulation {
+
+/* S1AP Emulation, runs on top of S1AP_CodecPort. It multiplexes/demultiplexes
+ * the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
+ * each of them.
+ *
+ * The S1AP_Emulation.main() function processes S1AP primitives from the S1AP
+ * socket via the S1AP_CodecPort, and dispatches them to the per-IMSI components.
+ *
+ * For each new IMSI, the S1apOps.create_cb() is called. It can create
+ * or resolve a TTCN-3 component, and returns a component reference to which that IMSI
+ * is routed/dispatched.
+ *
+ * If a pre-existing component wants to register to handle a future inbound IMSI, it can
+ * do so by registering an "expect" with the expected IMSI.
+ *
+ * Inbound non-UE related S1AP messages (such as RESET, SETUP, OVERLOAD) are dispatched to
+ * the S1apOps.unitdata_cb() callback, which is registered with an argument to the
+ * main() function below.
+ *
+ * (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ * All rights reserved.
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import from S1AP_CodecPort all;
+import from S1AP_CodecPort_CtrlFunct all;
+import from S1AP_Types all;
+import from S1AP_Constants all;
+import from S1AP_PDU_Contents all;
+import from S1AP_PDU_Descriptions all;
+import from S1AP_IEs all;
+import from S1AP_Templates all;
+
+import from NAS_EPS_Types all;
+import from NAS_Templates all;
+
+import from LTE_CryptoFunctions all;
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+import from DNS_Helpers all;
+
+
+type component S1AP_ConnHdlr {
+ port S1AP_Conn_PT S1AP;
+ /* procedure based port to register for incoming connections */
+ port S1APEM_PROC_PT S1AP_PROC;
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port S1AP_Conn_PT message {
+ inout S1AP_PDU, PDU_NAS_EPS, S1APEM_Config;
+} with { extension "internal" };
+
+type record NAS_Keys {
+ octetstring k_nas_int,
+ octetstring k_nas_enc
+};
+type union S1APEM_Config {
+ NAS_Keys set_nas_keys
+};
+
+type enumerated S1APEM_EventUpDown {
+ S1APEM_EVENT_DOWN,
+ S1APEM_EVENT_UP
+}
+
+/* an event indicating us whether or not a connection is physically up or down,
+ * and whether we have received an ID_ACK */
+type union S1APEM_Event {
+ S1APEM_EventUpDown up_down
+}
+
+/* global test port e.g. for non-imsi/conn specific messages */
+type port S1AP_PT message {
+ inout S1AP_PDU, S1APEM_Event;
+} with { extension "internal" };
+
+
+/* represents a single S1AP Association */
+type record AssociationData {
+ S1AP_ConnHdlr comp_ref, /* component handling this UE connection */
+ uint24_t enb_ue_s1ap_id optional, /* eNB side S1AP ID */
+ uint32_t mme_ue_s1ap_id optional, /* MME side S1AP ID */
+ EUTRAN_CGI cgi optional,
+ TAI tai optional,
+ NAS_UE_State nus
+
+ //hexstring imsi optional
+};
+
+type component S1AP_Emulation_CT {
+ /* Port facing to the UDP SUT */
+ port S1AP_CODEC_PT S1AP;
+ /* All S1AP_ConnHdlr S1AP ports connect here
+ * S1AP_Emulation_CT.main needs to figure out what messages
+ * to send where with CLIENT.send() to vc_conn */
+ port S1AP_Conn_PT S1AP_CLIENT;
+ /* currently tracked connections */
+ var AssociationData S1apAssociationTable[16];
+ /* pending expected CRCX */
+ var ExpectData S1apExpectTable[8];
+ /* procedure based port to register for incoming connections */
+ port S1APEM_PROC_PT S1AP_PROC;
+ /* test port for unit data messages */
+ port S1AP_PT S1AP_UNIT;
+
+ var S1AP_conn_parameters g_pars;
+ var charstring g_s1ap_id;
+ var integer g_s1ap_conn_id := -1;
+}
+
+type function S1APCreateCallback(S1AP_PDU msg, template (omit) MME_UE_S1AP_ID mme_id,
+ template (omit) ENB_UE_S1AP_ID enb_id, charstring id)
+runs on S1AP_Emulation_CT return S1AP_ConnHdlr;
+
+type function S1APUnitdataCallback(S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_PDU;
+
+type record S1APOps {
+ S1APCreateCallback create_cb,
+ S1APUnitdataCallback unitdata_cb
+}
+
+type record S1AP_conn_parameters {
+ HostName remote_ip,
+ PortNumber remote_sctp_port,
+ HostName local_ip,
+ PortNumber local_sctp_port,
+ NAS_Role role
+}
+
+function tr_S1AP_RecvFrom_R(template S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_RecvFrom {
+ var template S1AP_RecvFrom mrf := {
+ connId := g_s1ap_conn_id,
+ remName := ?,
+ remPort := ?,
+ locName := ?,
+ locPort := ?,
+ msg := msg
+ }
+ return mrf;
+}
+
+private function f_s1ap_ids_known(template (omit) MME_UE_S1AP_ID mme_id,
+ template (omit) ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return boolean {
+ var integer i;
+ log("f_s1ap_ids_known(",mme_id,", ",enb_id,")");
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ log("tbl[",i,"]: mme=", S1apAssociationTable[i].mme_ue_s1ap_id,
+ ", enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
+ /* skip empty records */
+ if (S1apAssociationTable[i].mme_ue_s1ap_id == omit and
+ S1apAssociationTable[i].enb_ue_s1ap_id == omit) {
+ log("skipping empty ", i);
+ continue;
+ }
+ if (S1apAssociationTable[i].mme_ue_s1ap_id == omit) {
+ log("entry ", i, " has no MME ID yet (enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
+ /* Table doesn't yet know the MME side ID, let's look-up only
+ * based on the eNB side ID */
+ if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
+ /* update table with MME side ID */
+ S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
+ return true;
+ }
+ } else if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id) and
+ match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+private function f_comp_known(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return boolean {
+ var integer i;
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ if (S1apAssociationTable[i].comp_ref == client) {
+ return true;
+ }
+ }
+ return false;
+}
+
+private function f_assoc_id_by_s1ap_ids(template (omit) MME_UE_S1AP_ID mme_id,
+ template (omit) ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return integer {
+ var integer i;
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
+ if (istemplatekind(mme_id, "omit")) {
+ return i;
+ } else {
+ if (match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
+ return i;
+ }
+ }
+ }
+ }
+ setverdict(fail, "S1AP Association Table not found by ENB-ID=", enb_id, " MME-ID=", mme_id);
+ mtc.stop;
+}
+
+private function f_assoc_id_by_comp(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return integer {
+ var integer i;
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ if (S1apAssociationTable[i].comp_ref == client) {
+ return i;
+ }
+ }
+ setverdict(fail, "S1AP Association Table not found by component ", client);
+ mtc.stop;
+}
+
+private function f_assoc_by_comp(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return AssociationData {
+ var integer i := f_assoc_id_by_comp(client);
+ return S1apAssociationTable[i];
+}
+
+private function f_s1ap_id_table_add(S1AP_ConnHdlr comp_ref,
+ template (omit) MME_UE_S1AP_ID mme_id, ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return integer {
+ var integer i;
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ if (not isvalue(S1apAssociationTable[i].enb_ue_s1ap_id)) {
+ S1apAssociationTable[i].enb_ue_s1ap_id := enb_id;
+ if (istemplatekind(mme_id, "omit")) {
+ S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+ } else {
+ S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
+ }
+ S1apAssociationTable[i].comp_ref := comp_ref;
+ return i;
+ }
+ }
+ testcase.stop("S1AP Association Table full!");
+ return -1;
+}
+
+private function f_s1ap_id_table_del(S1AP_ConnHdlr comp_ref, ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ if (S1apAssociationTable[i].comp_ref == comp_ref and
+ S1apAssociationTable[i].mme_ue_s1ap_id == enb_id) {
+ S1apAssociationTable[i].enb_ue_s1ap_id := omit;
+ S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+ S1apAssociationTable[i].comp_ref := null;
+ return;
+ }
+ }
+ setverdict(fail, "S1AP Association Table: Couldn't find to-be-deleted entry!");
+ mtc.stop;
+}
+
+
+private function f_s1ap_id_table_init()
+runs on S1AP_Emulation_CT {
+ for (var integer i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+ S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+ S1apAssociationTable[i].enb_ue_s1ap_id := omit;
+ S1apAssociationTable[i].cgi := omit;
+ S1apAssociationTable[i].tai := omit;
+ S1apAssociationTable[i].nus := valueof(t_NAS_UE_State(g_pars.role));
+ }
+}
+
+private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := 18) := {
+ sinfo_stream := omit,
+ sinfo_ppid := ppid,
+ remSocks := omit,
+ assocId := omit
+};
+
+private template PortEvent tr_SctpAssocChange := {
+ sctpEvent := {
+ sctpAssocChange := ?
+ }
+}
+private template PortEvent tr_SctpPeerAddrChange := {
+ sctpEvent := {
+ sctpPeerAddrChange := ?
+ }
+}
+
+private function f_s1ap_xceive(template (value) S1AP_PDU tx,
+ template S1AP_PDU rx_t := ?)
+runs on S1AP_Emulation_CT return S1AP_PDU {
+ timer T := 10.0;
+ var S1AP_RecvFrom mrf;
+
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id, tx));
+ alt {
+ [] S1AP.receive(tr_S1AP_RecvFrom_R(rx_t)) -> value mrf { }
+ [] S1AP.receive(tr_SctpAssocChange) { repeat; }
+ [] S1AP.receive(tr_SctpPeerAddrChange) { repeat; }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ", rx_t);
+ mtc.stop;
+ }
+ }
+ return mrf.msg;
+}
+
+/*
+private function f_nas_try_decaps(PDU_NAS_EPS nas) return PDU_NAS_EPS
+{
+ var PDU_NAS_EPS_SecurityProtectedNASMessage secp_nas;
+ if (not match(nas, tr_NAS_EMM_SecurityProtected)) {
+ return nas;
+ }
+ secp_nas := nas.ePS_messages.ePS_MobilityManagement.pDU_NAS_EPS_SecurityProtectedNASMessage;
+ select (secp_nas.securityHeaderType) {
+ case ('0011'B) {
+ var octetstring knas_int := '530ce32318f26264eab26bc116870b86'O;
+ var octetstring data_with_seq := int2oct(secp_nas.sequenceNumber, 1) & secp_nas.nAS_Message;
+ var OCT4 exp_mac := f_snow_3g_f9(knas_int, secp_nas.sequenceNumber, 0,
+ is_downlink:=true, data:=data_with_seq);
+ if (exp_mac != secp_nas.messageAuthenticationCode) {
+ setverdict(fail, "Received NAS MAC ", secp_nas.messageAuthenticationCode,
+ " doesn't match expected MAC ", exp_mac, ": ", nas);
+ mtc.stop;
+ }
+ return dec_PDU_NAS_EPS(secp_nas.nAS_Message);
+ }
+ case else {
+ setverdict(fail, "Implement SecHdrType for ", secp_nas);
+ mtc.stop;
+ }
+ }
+}
+*/
+
+function main(S1APOps ops, S1AP_conn_parameters p, charstring id) runs on S1AP_Emulation_CT {
+ var Result res;
+ g_pars := p;
+ g_s1ap_id := id;
+ f_s1ap_id_table_init();
+ f_expect_table_init();
+
+ map(self:S1AP, system:S1AP_CODEC_PT);
+ if (p.remote_sctp_port == -1) {
+ res := S1AP_CodecPort_CtrlFunct.f_IPL4_listen(S1AP, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
+ } else {
+ res := S1AP_CodecPort_CtrlFunct.f_IPL4_connect(S1AP, p.remote_ip, p.remote_sctp_port,
+ p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
+ }
+ if (not ispresent(res.connId)) {
+ setverdict(fail, "Could not connect S1AP socket, check your configuration");
+ mtc.stop;
+ }
+ g_s1ap_conn_id := res.connId;
+
+ /* notify user about SCTP establishment */
+ if (p.remote_sctp_port != -1) {
+ S1AP_UNIT.send(S1APEM_Event:{up_down:=S1APEM_EVENT_UP})
+ }
+
+ while (true) {
+ var S1AP_ConnHdlr vc_conn;
+ var PDU_NAS_EPS nas;
+ var hexstring imsi;
+ var S1AP_RecvFrom mrf;
+ var S1AP_PDU msg;
+ var S1APEM_Config s1cfg;
+ var charstring vlr_name, mme_name;
+ var integer ai;
+
+ alt {
+ /* Configuration primitive from client */
+ [] S1AP_CLIENT.receive(S1APEM_Config:{set_nas_keys:=?}) -> value s1cfg sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ S1apAssociationTable[assoc_id].nus.k_nas_int := s1cfg.set_nas_keys.k_nas_int;
+ S1apAssociationTable[assoc_id].nus.k_nas_enc := s1cfg.set_nas_keys.k_nas_enc;
+ }
+ /* S1AP from client: InitialUE */
+ [] S1AP_CLIENT.receive(tr_S1AP_InitialUE) -> value msg sender vc_conn {
+ /* create a table entry about this connection */
+ ai := f_s1ap_id_table_add(vc_conn, omit, valueof(f_S1AP_get_ENB_UE_S1AP_ID(msg)));
+ /* Store CGI + TAI so we can use it for generating UlNasTransport from NAS */
+ S1apAssociationTable[ai].tai := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.TAI;
+ S1apAssociationTable[ai].cgi := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[3].value_.EUTRAN_CGI;
+ /* Pass message through */
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+ }
+ /* NAS from client: Wrap in S1AP Uplink NAS Transport */
+ [] S1AP_CLIENT.receive(PDU_NAS_EPS:?) -> value nas sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ var AssociationData ad := S1apAssociationTable[assoc_id];
+ nas := f_nas_encaps(S1apAssociationTable[assoc_id].nus, nas, new_ctx := false);
+ var octetstring nas_enc := enc_PDU_NAS_EPS(nas);
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id,
+ ts_S1AP_UlNasTransport(ad.mme_ue_s1ap_id,
+ ad.enb_ue_s1ap_id,
+ nas_enc, ad.cgi, ad.tai)));
+ }
+ /* S1AP from client: pass on transparently */
+ [] S1AP_CLIENT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
+ /* Pass message through */
+ /* FIXME: validate S1AP_IDs ? */
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+ }
+
+ /* non-UE related S1AP: pass through unmodified/unverified */
+ [] S1AP_UNIT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
+ /* Pass message through */
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+ }
+
+ /* S1AP received from peer (MME) */
+ [] S1AP.receive(tr_S1AP_RecvFrom_R(?)) -> value mrf {
+ if (match(mrf.msg, tr_S1AP_nonUErelated)) {
+ /* non-UE-related S1AP message */
+ var template S1AP_PDU resp := ops.unitdata_cb.apply(mrf.msg);
+ if (isvalue(resp)) {
+ S1AP.send(t_S1AP_Send(g_s1ap_conn_id, valueof(resp)));
+ }
+ } else if (match(mrf.msg, tr_S1AP_UeContextReleaseCmd)) {
+ /* TODO: special handling, as it contains multiple eNB or MME IDs */
+ setverdict(fail, "implement UeContextReleaseCmd handling");
+ mtc.stop;
+ } else {
+ /* Ue-related S1AP message */
+ /* obtain MME + ENB UE S1AP ID */
+ var template (omit) MME_UE_S1AP_ID mme_ue_id := f_S1AP_get_MME_UE_S1AP_ID(mrf.msg);
+ var template (omit) ENB_UE_S1AP_ID enb_ue_id := f_S1AP_get_ENB_UE_S1AP_ID(mrf.msg);
+ /* check if those IDs are known in our table */
+ if (f_s1ap_ids_known(mme_ue_id, enb_ue_id)) {
+ /* if yes, dispatch to the ConnHdlr for this Ue-Connection */
+ var template (omit) octetstring nas_enc;
+ var integer assoc_id := f_assoc_id_by_s1ap_ids(mme_ue_id, enb_ue_id);
+ vc_conn := S1apAssociationTable[assoc_id].comp_ref;
+ nas_enc := f_S1AP_get_NAS_PDU(mrf.msg);
+ if (isvalue(nas_enc)) {
+ nas := dec_PDU_NAS_EPS(valueof(nas_enc));
+ if (match(nas, tr_NAS_EMM_SecurityProtected)) {
+ nas := f_nas_try_decaps(S1apAssociationTable[assoc_id].nus, nas);
+ }
+ /* send decoded NAS */
+ S1AP_CLIENT.send(nas) to vc_conn;
+ } else {
+ /* send raw S1AP */
+ S1AP_CLIENT.send(mrf.msg) to vc_conn;
+ }
+ } else {
+ /* if not, call create_cb so it can create new ConnHdlr */
+ vc_conn := ops.create_cb.apply(mrf.msg, mme_ue_id, enb_ue_id, id);
+ f_s1ap_id_table_add(vc_conn, mme_ue_id, valueof(enb_ue_id));
+ S1AP_CLIENT.send(mrf.msg) to vc_conn;
+ }
+ }
+ }
+ [] S1AP.receive(tr_SctpAssocChange) { }
+ [] S1AP.receive(tr_SctpPeerAddrChange) { }
+ [] S1AP_PROC.getcall(S1APEM_register:{?,?}) -> param(imsi, vc_conn) {
+ f_create_expect(imsi, vc_conn);
+ S1AP_PROC.reply(S1APEM_register:{imsi, vc_conn}) to vc_conn;
+ }
+ }
+
+ }
+}
+
+/* "Expect" Handling */
+
+type record ExpectData {
+ hexstring imsi optional,
+ S1AP_ConnHdlr vc_conn
+}
+
+signature S1APEM_register(in hexstring imsi, in S1AP_ConnHdlr hdlr);
+
+type port S1APEM_PROC_PT procedure {
+ inout S1APEM_register;
+} with { extension "internal" };
+
+/* Function that can be used as create_cb and will usse the expect table */
+function ExpectedCreateCallback(S1AP_PDU msg, hexstring imsi, charstring id)
+runs on S1AP_Emulation_CT return S1AP_ConnHdlr {
+ var S1AP_ConnHdlr ret := null;
+ var integer i;
+
+ for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+ if (not ispresent(S1apExpectTable[i].imsi)) {
+ continue;
+ }
+ if (imsi == S1apExpectTable[i].imsi) {
+ ret := S1apExpectTable[i].vc_conn;
+ /* Release this entry */
+ S1apExpectTable[i].imsi := omit;
+ S1apExpectTable[i].vc_conn := null;
+ log("Found Expect[", i, "] for ", msg, " handled at ", ret);
+ return ret;
+ }
+ }
+ setverdict(fail, "Couldn't find Expect for ", msg);
+ mtc.stop;
+}
+
+private function f_create_expect(hexstring imsi, S1AP_ConnHdlr hdlr)
+runs on S1AP_Emulation_CT {
+ var integer i;
+
+ /* Check an entry like this is not already presnt */
+ for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+ if (imsi == S1apExpectTable[i].imsi) {
+ setverdict(fail, "IMSI already present", imsi);
+ mtc.stop;
+ }
+ }
+ for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+ if (not ispresent(S1apExpectTable[i].imsi)) {
+ S1apExpectTable[i].imsi := imsi;
+ S1apExpectTable[i].vc_conn := hdlr;
+ log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
+ return;
+ }
+ }
+ testcase.stop("No space left in S1apExpectTable")
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect in emulation */
+function f_create_s1ap_expect(hexstring imsi) runs on S1AP_ConnHdlr {
+ S1AP_PROC.call(S1APEM_register:{imsi, self}) {
+ [] S1AP_PROC.getreply(S1APEM_register:{?,?}) {};
+ }
+}
+
+
+private function f_expect_table_init()
+runs on S1AP_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(S1apExpectTable); i := i + 1) {
+ S1apExpectTable[i].imsi := omit;
+ }
+}
+
+function DummyUnitdataCallback(S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_PDU {
+ log("Ignoring S1AP ", msg);
+ return omit;
+}
+
+
+function f_S1AP_get_ENB_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) ENB_UE_S1AP_ID
+{
+ if (ischosen(s1ap.initiatingMessage)) {
+ var InitiatingMessage im := s1ap.initiatingMessage;
+ select (s1ap) {
+ case (tr_S1AP_InitialUE) {
+ return im.value_.InitialUEMessage.protocolIEs[0].value_.ENB_UE_S1AP_ID;
+ }
+ case (tr_S1AP_DlNasTransport) {
+ return im.value_.DownlinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UlNasTransport) {
+ return im.value_.UplinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ case (tr_S1AP_IntialCtxSetupReq) {
+ return im.value_.initialContextSetupRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UeContextReleaseReq) {
+ return im.value_.UEContextReleaseRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ /* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
+ case (tr_S1AP_ConnEstInd) {
+ return im.value_.ConnectionEstablishmentIndication.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ } else if (ischosen(s1ap.successfulOutcome)) {
+ var SuccessfulOutcome so := s1ap.successfulOutcome;
+ select (s1ap) {
+ case (tr_S1AP_InitialCtxSetupResp) {
+ return so.value_.initialContextSetupResponse.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UeContextReleaseCompl) {
+ return so.value_.UEContextReleaseComplete.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ } else if (ischosen(s1ap.unsuccessfulOutcome)) {
+ var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
+ select (s1ap) {
+ case (tr_S1AP_InitialCtxSetupFail) {
+ return uo.value_.initialContextSetupFailure.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ }
+ return omit;
+}
+
+function f_S1AP_get_MME_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) MME_UE_S1AP_ID
+{
+ if (ischosen(s1ap.initiatingMessage)) {
+ var InitiatingMessage im := s1ap.initiatingMessage;
+ select (s1ap) {
+ case (tr_S1AP_DlNasTransport) {
+ return im.value_.DownlinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UlNasTransport) {
+ return im.value_.UplinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ case (tr_S1AP_IntialCtxSetupReq) {
+ return im.value_.initialContextSetupRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UeContextReleaseReq) {
+ return im.value_.UEContextReleaseRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ /* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
+ case (tr_S1AP_ConnEstInd) {
+ return im.value_.ConnectionEstablishmentIndication.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ } else if (ischosen(s1ap.successfulOutcome)) {
+ var SuccessfulOutcome so := s1ap.successfulOutcome;
+ select (s1ap) {
+ case (tr_S1AP_InitialCtxSetupResp) {
+ return so.value_.initialContextSetupResponse.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ case (tr_S1AP_UeContextReleaseCompl) {
+ return so.value_.UEContextReleaseComplete.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ } else if (ischosen(s1ap.unsuccessfulOutcome)) {
+ var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
+ select (s1ap) {
+ case (tr_S1AP_InitialCtxSetupFail) {
+ return uo.value_.initialContextSetupFailure.protocolIEs[0].value_.MME_UE_S1AP_ID;
+ }
+ /* TODO */
+ }
+ }
+ return omit;
+}
+
+function f_S1AP_get_NAS_PDU(S1AP_PDU s1ap) return template (omit) NAS_PDU
+{
+ var integer i;
+
+ if (ischosen(s1ap.initiatingMessage)) {
+ var InitiatingMessage im := s1ap.initiatingMessage;
+ select (s1ap) {
+ case (tr_S1AP_DlNasTransport) {
+ var DownlinkNASTransport msg := im.value_.DownlinkNASTransport;
+ for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
+ if (msg.protocolIEs[i].id == id_NAS_PDU) {
+ return msg.protocolIEs[i].value_.NAS_PDU;
+ }
+ }
+ }
+ case (tr_S1AP_UlNasTransport) {
+ var UplinkNASTransport msg := im.value_.UplinkNASTransport;
+ for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
+ if (msg.protocolIEs[i].id == id_NAS_PDU) {
+ return msg.protocolIEs[i].value_.NAS_PDU;
+ }
+ }
+ }
+ }
+ }
+ return omit;
+}
+
+
+
+}