aboutsummaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2018-10-09 09:20:45 +0200
committerHarald Welte <laforge@gnumonks.org>2018-10-28 09:52:34 +0000
commitaf5bce4f97cbd212824e5a248815a4d968f1e6a6 (patch)
treee75816c6e188dfbdbb9ae50a177e3669af4e6e80 /library
parenta07cfd909ca708709652f9e9481bf909936669e0 (diff)
Add SGsAP_CodecPort + SGsAP_Emulation module
Diffstat (limited to 'library')
-rw-r--r--library/SGsAP_CodecPort.ttcn72
-rw-r--r--library/SGsAP_CodecPort_CtrlFunct.ttcn44
-rw-r--r--library/SGsAP_CodecPort_CtrlFunctDef.cc56
-rw-r--r--library/SGsAP_Emulation.ttcn418
4 files changed, 590 insertions, 0 deletions
diff --git a/library/SGsAP_CodecPort.ttcn b/library/SGsAP_CodecPort.ttcn
new file mode 100644
index 00000000..2981fa2b
--- /dev/null
+++ b/library/SGsAP_CodecPort.ttcn
@@ -0,0 +1,72 @@
+module SGsAP_CodecPort {
+
+/* Simple SGsAP Codec Port, translating between raw SCTP primitives with
+ * octetstring payload towards the IPL4asp provider, and SGsAP primitives
+ * which carry the decoded SGsAP data types as payload.
+ *
+ * (C) 2018 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.
+ */
+
+ import from IPL4asp_PortType all;
+ import from IPL4asp_Types all;
+ import from SGsAP_Types all;
+
+ type record SGsAP_RecvFrom {
+ ConnectionId connId,
+ HostName remName,
+ PortNumber remPort,
+ HostName locName,
+ PortNumber locPort,
+ PDU_SGsAP msg
+ };
+
+ template SGsAP_RecvFrom t_SGsAP_RecvFrom(template PDU_SGsAP msg) := {
+ connId := ?,
+ remName := ?,
+ remPort := ?,
+ locName := ?,
+ locPort := ?,
+ msg := msg
+ }
+
+ type record SGsAP_Send {
+ ConnectionId connId,
+ PDU_SGsAP msg
+ }
+
+ template SGsAP_Send t_SGsAP_Send(template ConnectionId connId, template PDU_SGsAP msg) := {
+ connId := connId,
+ msg := msg
+ }
+
+ private function IPL4_to_SGsAP_RecvFrom(in ASP_RecvFrom pin, out SGsAP_RecvFrom pout) {
+ pout.connId := pin.connId;
+ pout.remName := pin.remName;
+ pout.remPort := pin.remPort;
+ pout.locName := pin.locName;
+ pout.locPort := pin.locPort;
+ pout.msg := dec_PDU_SGsAP(pin.msg);
+ } with { extension "prototype(fast)" };
+
+ private function SGsAP_to_IPL4_Send(in SGsAP_Send pin, out ASP_Send pout) {
+ pout.connId := pin.connId;
+ pout.proto := { sctp := {} };
+ pout.msg := enc_PDU_SGsAP(pin.msg);
+ } with { extension "prototype(fast)" };
+
+ type port SGsAP_CODEC_PT message {
+ out SGsAP_Send;
+ in SGsAP_RecvFrom,
+ ASP_ConnId_ReadyToRelease,
+ ASP_Event;
+ } with { extension "user IPL4asp_PT
+ out(SGsAP_Send -> ASP_Send:function(SGsAP_to_IPL4_Send))
+ in(ASP_RecvFrom -> SGsAP_RecvFrom: function(IPL4_to_SGsAP_RecvFrom);
+ ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;
+ ASP_Event -> ASP_Event: simple)"
+ }
+}
diff --git a/library/SGsAP_CodecPort_CtrlFunct.ttcn b/library/SGsAP_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 00000000..b09fc946
--- /dev/null
+++ b/library/SGsAP_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,44 @@
+module SGsAP_CodecPort_CtrlFunct {
+
+ import from SGsAP_CodecPort all;
+ import from IPL4asp_Types all;
+
+ external function f_IPL4_listen(
+ inout SGsAP_CODEC_PT portRef,
+ in HostName locName,
+ in PortNumber locPort,
+ in ProtoTuple proto,
+ in OptionList options := {}
+ ) return Result;
+
+ external function f_IPL4_connect(
+ inout SGsAP_CODEC_PT portRef,
+ in HostName remName,
+ in PortNumber remPort,
+ in HostName locName,
+ in PortNumber locPort,
+ in ConnectionId connId,
+ in ProtoTuple proto,
+ in OptionList options := {}
+ ) return Result;
+
+ external function f_IPL4_close(
+ inout SGsAP_CODEC_PT portRef,
+ in ConnectionId id,
+ in ProtoTuple proto := { unspecified := {} }
+ ) return Result;
+
+ external function f_IPL4_setUserData(
+ inout SGsAP_CODEC_PT portRef,
+ in ConnectionId id,
+ in UserData userData
+ ) return Result;
+
+ external function f_IPL4_getUserData(
+ inout SGsAP_CODEC_PT portRef,
+ in ConnectionId id,
+ out UserData userData
+ ) return Result;
+
+}
+
diff --git a/library/SGsAP_CodecPort_CtrlFunctDef.cc b/library/SGsAP_CodecPort_CtrlFunctDef.cc
new file mode 100644
index 00000000..aa38e512
--- /dev/null
+++ b/library/SGsAP_CodecPort_CtrlFunctDef.cc
@@ -0,0 +1,56 @@
+#include "IPL4asp_PortType.hh"
+#include "SGsAP_CodecPort.hh"
+#include "IPL4asp_PT.hh"
+
+namespace SGsAP__CodecPort__CtrlFunct {
+
+ IPL4asp__Types::Result f__IPL4__listen(
+ SGsAP__CodecPort::SGsAP__CODEC__PT& portRef,
+ const IPL4asp__Types::HostName& locName,
+ const IPL4asp__Types::PortNumber& locPort,
+ const IPL4asp__Types::ProtoTuple& proto,
+ const IPL4asp__Types::OptionList& options)
+ {
+ return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);
+ }
+
+ IPL4asp__Types::Result f__IPL4__connect(
+ SGsAP__CodecPort::SGsAP__CODEC__PT& portRef,
+ const IPL4asp__Types::HostName& remName,
+ const IPL4asp__Types::PortNumber& remPort,
+ const IPL4asp__Types::HostName& locName,
+ const IPL4asp__Types::PortNumber& locPort,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::ProtoTuple& proto,
+ const IPL4asp__Types::OptionList& options)
+ {
+ return f__IPL4__PROVIDER__connect(portRef, remName, remPort,
+ locName, locPort, connId, proto, options);
+ }
+
+ IPL4asp__Types::Result f__IPL4__close(
+ SGsAP__CodecPort::SGsAP__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::ProtoTuple& proto)
+ {
+ return f__IPL4__PROVIDER__close(portRef, connId, proto);
+ }
+
+ IPL4asp__Types::Result f__IPL4__setUserData(
+ SGsAP__CodecPort::SGsAP__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::UserData& userData)
+ {
+ return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);
+ }
+
+ IPL4asp__Types::Result f__IPL4__getUserData(
+ SGsAP__CodecPort::SGsAP__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ IPL4asp__Types::UserData& userData)
+ {
+ return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+ }
+
+}
+
diff --git a/library/SGsAP_Emulation.ttcn b/library/SGsAP_Emulation.ttcn
new file mode 100644
index 00000000..0c37840b
--- /dev/null
+++ b/library/SGsAP_Emulation.ttcn
@@ -0,0 +1,418 @@
+module SGsAP_Emulation {
+
+/* SGsAP Emulation, runs on top of SGsAP_CodecPort. It multiplexes/demultiplexes
+ * the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
+ * each of them.
+ *
+ * The SGsAP_Emulation.main() function processes SGsAP primitives from the SGsAP
+ * socket via the SGsAP_CodecPort, and dispatches them to the per-IMSI components.
+ *
+ * For each new IMSI, the SgsapOps.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 SGsAP messages without IMSI (such as RESET-IND/ACK) are dispatched to
+ * the SgsapOps.unitdata_cb() callback, which is registered with an argument to the
+ * main() function below.
+ *
+ * (C) 2018 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.
+ */
+
+import from SGsAP_CodecPort all;
+import from SGsAP_CodecPort_CtrlFunct all;
+import from SGsAP_Types all;
+import from SGsAP_Templates all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+
+type component SGsAP_ConnHdlr {
+ port SGsAP_Conn_PT SGsAP;
+ /* procedure based port to register for incoming connections */
+ port SGsAPEM_PROC_PT SGsAP_PROC;
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port SGsAP_Conn_PT message {
+ inout PDU_SGsAP;
+} with { extension "internal" };
+
+/* represents a single SGsAP Association */
+type record AssociationData {
+ SGsAP_ConnHdlr comp_ref,
+ hexstring imsi optional
+};
+
+type component SGsAP_Emulation_CT {
+ /* Port facing to the UDP SUT */
+ port SGsAP_CODEC_PT SGsAP;
+ /* All SGsAP_ConnHdlr SGsAP ports connect here
+ * SGsAP_Emulation_CT.main needs to figure out what messages
+ * to send where with CLIENT.send() to vc_conn */
+ port SGsAP_Conn_PT SGsAP_CLIENT;
+ /* currently tracked connections */
+ var AssociationData SgsapAssociationTable[16];
+ /* pending expected CRCX */
+ var ExpectData SgsapExpectTable[8];
+ /* procedure based port to register for incoming connections */
+ port SGsAPEM_PROC_PT SGsAP_PROC;
+
+ var charstring g_sgsap_id;
+ var integer g_sgsap_conn_id := -1;
+}
+
+type function SGsAPCreateCallback(PDU_SGsAP msg, hexstring imsi, charstring id)
+runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr;
+
+type function SGsAPUnitdataCallback(PDU_SGsAP msg)
+runs on SGsAP_Emulation_CT return template PDU_SGsAP;
+
+type record SGsAPOps {
+ SGsAPCreateCallback create_cb,
+ SGsAPUnitdataCallback unitdata_cb
+}
+
+type record SGsAP_conn_parameters {
+ HostName remote_ip,
+ PortNumber remote_sctp_port,
+ HostName local_ip,
+ PortNumber local_sctp_port
+}
+
+function tr_SGsAP_RecvFrom_R(template PDU_SGsAP msg)
+runs on SGsAP_Emulation_CT return template SGsAP_RecvFrom {
+ var template SGsAP_RecvFrom mrf := {
+ connId := g_sgsap_conn_id,
+ remName := ?,
+ remPort := ?,
+ locName := ?,
+ locPort := ?,
+ msg := msg
+ }
+ return mrf;
+}
+
+private function f_imsi_known(hexstring imsi)
+runs on SGsAP_Emulation_CT return boolean {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (SgsapAssociationTable[i].imsi == imsi) {
+ return true;
+ }
+ }
+ return false;
+}
+
+private function f_comp_known(SGsAP_ConnHdlr client)
+runs on SGsAP_Emulation_CT return boolean {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (SgsapAssociationTable[i].comp_ref == client) {
+ return true;
+ }
+ }
+ return false;
+}
+
+private function f_comp_by_imsi(hexstring imsi)
+runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (SgsapAssociationTable[i].imsi == imsi) {
+ return SgsapAssociationTable[i].comp_ref;
+ }
+ }
+ setverdict(fail, "SGsAP Association Table not found by IMSI", imsi);
+ mtc.stop;
+}
+
+private function f_imsi_by_comp(SGsAP_ConnHdlr client)
+runs on SGsAP_Emulation_CT return hexstring {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (SgsapAssociationTable[i].comp_ref == client) {
+ return SgsapAssociationTable[i].imsi;
+ }
+ }
+ setverdict(fail, "SGsAP Association Table not found by component ", client);
+ mtc.stop;
+}
+
+private function f_imsi_table_add(SGsAP_ConnHdlr comp_ref, hexstring imsi)
+runs on SGsAP_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (not isvalue(SgsapAssociationTable[i].imsi)) {
+ SgsapAssociationTable[i].imsi := imsi;
+ SgsapAssociationTable[i].comp_ref := comp_ref;
+ return;
+ }
+ }
+ testcase.stop("SGsAP Association Table full!");
+}
+
+private function f_imsi_table_del(SGsAP_ConnHdlr comp_ref, hexstring imsi)
+runs on SGsAP_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ if (SgsapAssociationTable[i].comp_ref == comp_ref and
+ SgsapAssociationTable[i].imsi == imsi) {
+ SgsapAssociationTable[i].imsi := omit;
+ SgsapAssociationTable[i].comp_ref := null;
+ return;
+ }
+ }
+ setverdict(fail, "SGsAP Association Table: Couldn't find to-be-deleted entry!");
+ mtc.stop;
+}
+
+
+private function f_imsi_table_init()
+runs on SGsAP_Emulation_CT {
+ for (var integer i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
+ SgsapAssociationTable[i].comp_ref := null;
+ SgsapAssociationTable[i].imsi := omit;
+ }
+}
+
+private function f_SGsAP_get_imsi(PDU_SGsAP pdu) return template (omit) IMSI
+{
+ if (ischosen(pdu.sGsAP_ALERT_ACK)) {
+ return pdu.sGsAP_ALERT_ACK.iMSI;
+ } else if (ischosen(pdu.sGsAP_ALERT_REJECT)) {
+ return pdu.sGsAP_ALERT_REJECT.iMSI;
+ } else if (ischosen(pdu.sGsAP_ALERT_REQUEST)) {
+ return pdu.sGsAP_ALERT_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_DOWNLINK_UNITDATA)) {
+ return pdu.sGsAP_DOWNLINK_UNITDATA.iMSI;
+ } else if (ischosen(pdu.sGsAP_EPS_DETACH_ACK)) {
+ return pdu.sGsAP_EPS_DETACH_ACK.iMSI;
+ } else if (ischosen(pdu.sGsAP_EPS_DETACH_INDICATION)) {
+ return pdu.sGsAP_EPS_DETACH_INDICATION.iMSI;
+ } else if (ischosen(pdu.sGsAP_IMSI_DETACH_ACK)) {
+ return pdu.sGsAP_IMSI_DETACH_ACK.iMSI;
+ } else if (ischosen(pdu.sGsAP_IMSI_DETACH_INDICATION)) {
+ return pdu.sGsAP_IMSI_DETACH_INDICATION.iMSI;
+ } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_ACCEPT)) {
+ return pdu.sGsAP_LOCATION_UPDATE_ACCEPT.iMSI;
+ } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_REJECT)) {
+ return pdu.sGsAP_LOCATION_UPDATE_REJECT.iMSI;
+ } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_REQUEST)) {
+ return pdu.sGsAP_LOCATION_UPDATE_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_MM_INFORMATION_REQUEST)) {
+ return pdu.sGsAP_MM_INFORMATION_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_PAGING_REJECT)) {
+ return pdu.sGsAP_PAGING_REJECT.iMSI;
+ } else if (ischosen(pdu.sGsAP_PAGING_REQUEST)) {
+ return pdu.sGsAP_PAGING_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_SERVICE_REQUEST)) {
+ return pdu.sGsAP_SERVICE_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_STATUS)) {
+ return pdu.sGsAP_STATUS.iMSI;
+ } else if (ischosen(pdu.sGsAP_TMSI_REALLOCATION_COMPLETE)) {
+ return pdu.sGsAP_TMSI_REALLOCATION_COMPLETE.iMSI;
+ } else if (ischosen(pdu.sGsAP_UE_ACTIVITY_INDICATION)) {
+ return pdu.sGsAP_UE_ACTIVITY_INDICATION.iMSI;
+ } else if (ischosen(pdu.sGsAP_UE_UNREACHABLE)) {
+ return pdu.sGsAP_UE_UNREACHABLE.iMSI;
+ } else if (ischosen(pdu.sGsAP_UPLINK_UNITDATA)) {
+ return pdu.sGsAP_UPLINK_UNITDATA.iMSI;
+ } else if (ischosen(pdu.sGsAP_RELEASE_REQUEST)) {
+ return pdu.sGsAP_RELEASE_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_SERVICE_ABORT_REQUEST)) {
+ return pdu.sGsAP_SERVICE_ABORT_REQUEST.iMSI;
+ } else if (ischosen(pdu.sGsAP_MO_CSFB_INDICATION)) {
+ return pdu.sGsAP_MO_CSFB_INDICATION.iMSI;
+ }
+ return omit;
+}
+
+private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
+ 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_sgsap_xceive(template (value) PDU_SGsAP tx,
+ template PDU_SGsAP rx_t := ?)
+runs on SGsAP_Emulation_CT return PDU_SGsAP {
+ timer T := 10.0;
+ var SGsAP_RecvFrom mrf;
+
+ SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, tx));
+ alt {
+ [] SGsAP.receive(tr_SGsAP_RecvFrom_R(rx_t)) -> value mrf { }
+ [] SGsAP.receive(tr_SctpAssocChange) { repeat; }
+ [] SGsAP.receive(tr_SctpPeerAddrChange) { repeat; }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ", rx_t);
+ mtc.stop;
+ }
+ }
+ return mrf.msg;
+}
+
+function main(SGsAPOps ops, SGsAP_conn_parameters p, charstring id) runs on SGsAP_Emulation_CT {
+ var Result res;
+ g_sgsap_id := id;
+ f_imsi_table_init();
+ f_expect_table_init();
+
+ map(self:SGsAP, system:SGsAP_CODEC_PT);
+ if (p.remote_sctp_port == -1) {
+ res := SGsAP_CodecPort_CtrlFunct.f_IPL4_listen(SGsAP, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
+ } else {
+ res := SGsAP_CodecPort_CtrlFunct.f_IPL4_connect(SGsAP, 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 SGsAP socket, check your configuration");
+ mtc.stop;
+ }
+ g_sgsap_conn_id := res.connId;
+
+ while (true) {
+ var SGsAP_ConnHdlr vc_conn;
+ var template IMSI imsi_t;
+ var hexstring imsi;
+ var SGsAP_RecvFrom mrf;
+ var PDU_SGsAP msg;
+
+ alt {
+ /* SGsAP from client */
+ [] SGsAP_CLIENT.receive(PDU_SGsAP:?) -> value msg sender vc_conn {
+ /* Pass message through */
+ /* TODO: check which ConnectionID client has allocated + store in table? */
+ SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
+ }
+ [] SGsAP.receive(tr_SGsAP_RecvFrom_R(?)) -> value mrf {
+ imsi_t := f_SGsAP_get_imsi(mrf.msg);
+ if (isvalue(imsi_t)) {
+ imsi := valueof(imsi_t.iMSI.digits);
+ if (f_imsi_known(imsi)) {
+ vc_conn := f_comp_by_imsi(imsi);
+ SGsAP_CLIENT.send(mrf.msg) to vc_conn;
+ } else {
+ vc_conn := ops.create_cb.apply(mrf.msg, imsi, id);
+ f_imsi_table_add(vc_conn, imsi);
+ SGsAP_CLIENT.send(mrf.msg) to vc_conn;
+ }
+ } else {
+ /* message contained no IMSI; is not IMSI-oriented */
+ var template PDU_SGsAP resp := ops.unitdata_cb.apply(mrf.msg);
+ if (isvalue(resp)) {
+ SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, valueof(resp)));
+ }
+ }
+ }
+ [] SGsAP.receive(tr_SctpAssocChange) { }
+ [] SGsAP.receive(tr_SctpPeerAddrChange) { }
+ [] SGsAP_PROC.getcall(SGsAPEM_register:{?,?}) -> param(imsi, vc_conn) {
+ f_create_expect(imsi, vc_conn);
+ SGsAP_PROC.reply(SGsAPEM_register:{imsi, vc_conn}) to vc_conn;
+ }
+ }
+
+ }
+}
+
+/* "Expect" Handling */
+
+type record ExpectData {
+ hexstring imsi optional,
+ SGsAP_ConnHdlr vc_conn
+}
+
+signature SGsAPEM_register(in hexstring imsi, in SGsAP_ConnHdlr hdlr);
+
+type port SGsAPEM_PROC_PT procedure {
+ inout SGsAPEM_register;
+} with { extension "internal" };
+
+/* Function that can be used as create_cb and will usse the expect table */
+function ExpectedCreateCallback(PDU_SGsAP msg, hexstring imsi, charstring id)
+runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr {
+ var SGsAP_ConnHdlr ret := null;
+ var integer i;
+
+ for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
+ if (not ispresent(SgsapExpectTable[i].imsi)) {
+ continue;
+ }
+ if (imsi == SgsapExpectTable[i].imsi) {
+ ret := SgsapExpectTable[i].vc_conn;
+ /* Release this entry */
+ SgsapExpectTable[i].imsi := omit;
+ SgsapExpectTable[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, SGsAP_ConnHdlr hdlr)
+runs on SGsAP_Emulation_CT {
+ var integer i;
+
+ /* Check an entry like this is not already presnt */
+ for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
+ if (imsi == SgsapExpectTable[i].imsi) {
+ setverdict(fail, "IMSI already present", imsi);
+ mtc.stop;
+ }
+ }
+ for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
+ if (not ispresent(SgsapExpectTable[i].imsi)) {
+ SgsapExpectTable[i].imsi := imsi;
+ SgsapExpectTable[i].vc_conn := hdlr;
+ log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
+ return;
+ }
+ }
+ testcase.stop("No space left in SgsapExpectTable")
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect in emulation */
+function f_create_sgsap_expect(hexstring imsi) runs on SGsAP_ConnHdlr {
+ SGsAP_PROC.call(SGsAPEM_register:{imsi, self}) {
+ [] SGsAP_PROC.getreply(SGsAPEM_register:{?,?}) {};
+ }
+}
+
+
+private function f_expect_table_init()
+runs on SGsAP_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(SgsapExpectTable); i := i + 1) {
+ SgsapExpectTable[i].imsi := omit;
+ }
+}
+
+function DummyUnitdataCallback(PDU_SGsAP msg)
+runs on SGsAP_Emulation_CT return template PDU_SGsAP {
+ log("Ignoring SGsAP ", msg);
+ return omit;
+}
+
+
+}