| 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].enb_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 use 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; |
| } |
| |
| |
| |
| } |