blob: f91e076a881fc7ae56e535125805658213fb6069 [file] [log] [blame]
Harald Welteaf5bce42018-10-09 09:20:45 +02001module SGsAP_Emulation {
2
3/* SGsAP Emulation, runs on top of SGsAP_CodecPort. It multiplexes/demultiplexes
4 * the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
5 * each of them.
6 *
7 * The SGsAP_Emulation.main() function processes SGsAP primitives from the SGsAP
8 * socket via the SGsAP_CodecPort, and dispatches them to the per-IMSI components.
9 *
10 * For each new IMSI, the SgsapOps.create_cb() is called. It can create
11 * or resolve a TTCN-3 component, and returns a component reference to which that IMSI
12 * is routed/dispatched.
13 *
14 * If a pre-existing component wants to register to handle a future inbound IMSI, it can
15 * do so by registering an "expect" with the expected IMSI.
16 *
17 * Inbound SGsAP messages without IMSI (such as RESET-IND/ACK) are dispatched to
18 * the SgsapOps.unitdata_cb() callback, which is registered with an argument to the
19 * main() function below.
20 *
21 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
22 * All rights reserved.
23 *
24 * Released under the terms of GNU General Public License, Version 2 or
25 * (at your option) any later version.
Harald Welte34b5a952019-05-27 11:54:11 +020026 *
27 * SPDX-License-Identifier: GPL-2.0-or-later
Harald Welteaf5bce42018-10-09 09:20:45 +020028 */
29
30import from SGsAP_CodecPort all;
31import from SGsAP_CodecPort_CtrlFunct all;
32import from SGsAP_Types all;
33import from SGsAP_Templates all;
34import from Osmocom_Types all;
35import from IPL4asp_Types all;
Harald Welte9a5dd542018-10-28 10:33:23 +010036import from DNS_Helpers all;
Harald Welte4263c522018-12-06 11:56:27 +010037import from MobileL3_Types all;
Harald Welteaf5bce42018-10-09 09:20:45 +020038
39type component SGsAP_ConnHdlr {
40 port SGsAP_Conn_PT SGsAP;
41 /* procedure based port to register for incoming connections */
42 port SGsAPEM_PROC_PT SGsAP_PROC;
43}
44
45/* port between individual per-connection components and this dispatcher */
46type port SGsAP_Conn_PT message {
Harald Welte4263c522018-12-06 11:56:27 +010047 inout PDU_SGsAP, PDU_ML3_MS_NW, PDU_ML3_NW_MS;
Harald Welteaf5bce42018-10-09 09:20:45 +020048} with { extension "internal" };
49
50/* represents a single SGsAP Association */
51type record AssociationData {
52 SGsAP_ConnHdlr comp_ref,
53 hexstring imsi optional
54};
55
56type component SGsAP_Emulation_CT {
57 /* Port facing to the UDP SUT */
58 port SGsAP_CODEC_PT SGsAP;
59 /* All SGsAP_ConnHdlr SGsAP ports connect here
60 * SGsAP_Emulation_CT.main needs to figure out what messages
61 * to send where with CLIENT.send() to vc_conn */
62 port SGsAP_Conn_PT SGsAP_CLIENT;
63 /* currently tracked connections */
64 var AssociationData SgsapAssociationTable[16];
65 /* pending expected CRCX */
66 var ExpectData SgsapExpectTable[8];
67 /* procedure based port to register for incoming connections */
68 port SGsAPEM_PROC_PT SGsAP_PROC;
69
70 var charstring g_sgsap_id;
71 var integer g_sgsap_conn_id := -1;
72}
73
74type function SGsAPCreateCallback(PDU_SGsAP msg, hexstring imsi, charstring id)
75runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr;
76
77type function SGsAPUnitdataCallback(PDU_SGsAP msg)
78runs on SGsAP_Emulation_CT return template PDU_SGsAP;
79
80type record SGsAPOps {
81 SGsAPCreateCallback create_cb,
82 SGsAPUnitdataCallback unitdata_cb
83}
84
85type record SGsAP_conn_parameters {
86 HostName remote_ip,
87 PortNumber remote_sctp_port,
88 HostName local_ip,
89 PortNumber local_sctp_port
90}
91
92function tr_SGsAP_RecvFrom_R(template PDU_SGsAP msg)
93runs on SGsAP_Emulation_CT return template SGsAP_RecvFrom {
94 var template SGsAP_RecvFrom mrf := {
95 connId := g_sgsap_conn_id,
96 remName := ?,
97 remPort := ?,
98 locName := ?,
99 locPort := ?,
100 msg := msg
101 }
102 return mrf;
103}
104
105private function f_imsi_known(hexstring imsi)
106runs on SGsAP_Emulation_CT return boolean {
107 var integer i;
108 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
109 if (SgsapAssociationTable[i].imsi == imsi) {
110 return true;
111 }
112 }
113 return false;
114}
115
116private function f_comp_known(SGsAP_ConnHdlr client)
117runs on SGsAP_Emulation_CT return boolean {
118 var integer i;
119 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
120 if (SgsapAssociationTable[i].comp_ref == client) {
121 return true;
122 }
123 }
124 return false;
125}
126
127private function f_comp_by_imsi(hexstring imsi)
128runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr {
129 var integer i;
130 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
131 if (SgsapAssociationTable[i].imsi == imsi) {
132 return SgsapAssociationTable[i].comp_ref;
133 }
134 }
135 setverdict(fail, "SGsAP Association Table not found by IMSI", imsi);
136 mtc.stop;
137}
138
139private function f_imsi_by_comp(SGsAP_ConnHdlr client)
140runs on SGsAP_Emulation_CT return hexstring {
141 var integer i;
142 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
143 if (SgsapAssociationTable[i].comp_ref == client) {
144 return SgsapAssociationTable[i].imsi;
145 }
146 }
147 setverdict(fail, "SGsAP Association Table not found by component ", client);
148 mtc.stop;
149}
150
151private function f_imsi_table_add(SGsAP_ConnHdlr comp_ref, hexstring imsi)
152runs on SGsAP_Emulation_CT {
153 var integer i;
154 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
155 if (not isvalue(SgsapAssociationTable[i].imsi)) {
156 SgsapAssociationTable[i].imsi := imsi;
157 SgsapAssociationTable[i].comp_ref := comp_ref;
158 return;
159 }
160 }
161 testcase.stop("SGsAP Association Table full!");
162}
163
164private function f_imsi_table_del(SGsAP_ConnHdlr comp_ref, hexstring imsi)
165runs on SGsAP_Emulation_CT {
166 var integer i;
167 for (i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
168 if (SgsapAssociationTable[i].comp_ref == comp_ref and
169 SgsapAssociationTable[i].imsi == imsi) {
170 SgsapAssociationTable[i].imsi := omit;
171 SgsapAssociationTable[i].comp_ref := null;
172 return;
173 }
174 }
175 setverdict(fail, "SGsAP Association Table: Couldn't find to-be-deleted entry!");
176 mtc.stop;
177}
178
179
180private function f_imsi_table_init()
181runs on SGsAP_Emulation_CT {
182 for (var integer i := 0; i < sizeof(SgsapAssociationTable); i := i+1) {
183 SgsapAssociationTable[i].comp_ref := null;
184 SgsapAssociationTable[i].imsi := omit;
185 }
186}
187
188private function f_SGsAP_get_imsi(PDU_SGsAP pdu) return template (omit) IMSI
189{
190 if (ischosen(pdu.sGsAP_ALERT_ACK)) {
191 return pdu.sGsAP_ALERT_ACK.iMSI;
192 } else if (ischosen(pdu.sGsAP_ALERT_REJECT)) {
193 return pdu.sGsAP_ALERT_REJECT.iMSI;
194 } else if (ischosen(pdu.sGsAP_ALERT_REQUEST)) {
195 return pdu.sGsAP_ALERT_REQUEST.iMSI;
196 } else if (ischosen(pdu.sGsAP_DOWNLINK_UNITDATA)) {
197 return pdu.sGsAP_DOWNLINK_UNITDATA.iMSI;
198 } else if (ischosen(pdu.sGsAP_EPS_DETACH_ACK)) {
199 return pdu.sGsAP_EPS_DETACH_ACK.iMSI;
200 } else if (ischosen(pdu.sGsAP_EPS_DETACH_INDICATION)) {
201 return pdu.sGsAP_EPS_DETACH_INDICATION.iMSI;
202 } else if (ischosen(pdu.sGsAP_IMSI_DETACH_ACK)) {
203 return pdu.sGsAP_IMSI_DETACH_ACK.iMSI;
204 } else if (ischosen(pdu.sGsAP_IMSI_DETACH_INDICATION)) {
205 return pdu.sGsAP_IMSI_DETACH_INDICATION.iMSI;
206 } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_ACCEPT)) {
207 return pdu.sGsAP_LOCATION_UPDATE_ACCEPT.iMSI;
208 } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_REJECT)) {
209 return pdu.sGsAP_LOCATION_UPDATE_REJECT.iMSI;
210 } else if (ischosen(pdu.sGsAP_LOCATION_UPDATE_REQUEST)) {
211 return pdu.sGsAP_LOCATION_UPDATE_REQUEST.iMSI;
212 } else if (ischosen(pdu.sGsAP_MM_INFORMATION_REQUEST)) {
213 return pdu.sGsAP_MM_INFORMATION_REQUEST.iMSI;
214 } else if (ischosen(pdu.sGsAP_PAGING_REJECT)) {
215 return pdu.sGsAP_PAGING_REJECT.iMSI;
216 } else if (ischosen(pdu.sGsAP_PAGING_REQUEST)) {
217 return pdu.sGsAP_PAGING_REQUEST.iMSI;
218 } else if (ischosen(pdu.sGsAP_SERVICE_REQUEST)) {
219 return pdu.sGsAP_SERVICE_REQUEST.iMSI;
220 } else if (ischosen(pdu.sGsAP_STATUS)) {
221 return pdu.sGsAP_STATUS.iMSI;
222 } else if (ischosen(pdu.sGsAP_TMSI_REALLOCATION_COMPLETE)) {
223 return pdu.sGsAP_TMSI_REALLOCATION_COMPLETE.iMSI;
224 } else if (ischosen(pdu.sGsAP_UE_ACTIVITY_INDICATION)) {
225 return pdu.sGsAP_UE_ACTIVITY_INDICATION.iMSI;
226 } else if (ischosen(pdu.sGsAP_UE_UNREACHABLE)) {
227 return pdu.sGsAP_UE_UNREACHABLE.iMSI;
228 } else if (ischosen(pdu.sGsAP_UPLINK_UNITDATA)) {
229 return pdu.sGsAP_UPLINK_UNITDATA.iMSI;
230 } else if (ischosen(pdu.sGsAP_RELEASE_REQUEST)) {
231 return pdu.sGsAP_RELEASE_REQUEST.iMSI;
232 } else if (ischosen(pdu.sGsAP_SERVICE_ABORT_REQUEST)) {
233 return pdu.sGsAP_SERVICE_ABORT_REQUEST.iMSI;
234 } else if (ischosen(pdu.sGsAP_MO_CSFB_INDICATION)) {
235 return pdu.sGsAP_MO_CSFB_INDICATION.iMSI;
236 }
237 return omit;
238}
239
240private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
241 sinfo_stream := omit,
242 sinfo_ppid := ppid,
243 remSocks := omit,
244 assocId := omit
245};
246
247private template PortEvent tr_SctpAssocChange := {
248 sctpEvent := {
249 sctpAssocChange := ?
250 }
251}
252private template PortEvent tr_SctpPeerAddrChange := {
253 sctpEvent := {
254 sctpPeerAddrChange := ?
255 }
256}
257
258private function f_sgsap_xceive(template (value) PDU_SGsAP tx,
259 template PDU_SGsAP rx_t := ?)
260runs on SGsAP_Emulation_CT return PDU_SGsAP {
261 timer T := 10.0;
262 var SGsAP_RecvFrom mrf;
263
264 SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, tx));
265 alt {
266 [] SGsAP.receive(tr_SGsAP_RecvFrom_R(rx_t)) -> value mrf { }
267 [] SGsAP.receive(tr_SctpAssocChange) { repeat; }
268 [] SGsAP.receive(tr_SctpPeerAddrChange) { repeat; }
269 [] T.timeout {
270 setverdict(fail, "Timeout waiting for ", rx_t);
271 mtc.stop;
272 }
273 }
274 return mrf.msg;
275}
276
277function main(SGsAPOps ops, SGsAP_conn_parameters p, charstring id) runs on SGsAP_Emulation_CT {
278 var Result res;
279 g_sgsap_id := id;
280 f_imsi_table_init();
281 f_expect_table_init();
282
283 map(self:SGsAP, system:SGsAP_CODEC_PT);
284 if (p.remote_sctp_port == -1) {
285 res := SGsAP_CodecPort_CtrlFunct.f_IPL4_listen(SGsAP, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
286 } else {
287 res := SGsAP_CodecPort_CtrlFunct.f_IPL4_connect(SGsAP, p.remote_ip, p.remote_sctp_port,
288 p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
289 }
290 if (not ispresent(res.connId)) {
291 setverdict(fail, "Could not connect SGsAP socket, check your configuration");
292 mtc.stop;
293 }
294 g_sgsap_conn_id := res.connId;
295
296 while (true) {
297 var SGsAP_ConnHdlr vc_conn;
Harald Welte4263c522018-12-06 11:56:27 +0100298 var PDU_ML3_MS_NW l3_mo;
299 var PDU_ML3_NW_MS l3_mt;
Harald Welteaf5bce42018-10-09 09:20:45 +0200300 var template IMSI imsi_t;
301 var hexstring imsi;
302 var SGsAP_RecvFrom mrf;
303 var PDU_SGsAP msg;
Harald Welte9a5dd542018-10-28 10:33:23 +0100304 var charstring vlr_name, mme_name;
Harald Welteaf5bce42018-10-09 09:20:45 +0200305
306 alt {
307 /* SGsAP from client */
308 [] SGsAP_CLIENT.receive(PDU_SGsAP:?) -> value msg sender vc_conn {
309 /* Pass message through */
310 /* TODO: check which ConnectionID client has allocated + store in table? */
311 SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
312 }
Harald Welte4263c522018-12-06 11:56:27 +0100313 /* DTAP/MobileL3 from client (emulated MS): wrap in SGsAP-UL-UD and send */
314 [] SGsAP_CLIENT.receive(PDU_ML3_MS_NW:?) -> value l3_mo sender vc_conn {
315 var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3_mo);
316 imsi := f_imsi_by_comp(vc_conn);
317 msg := valueof(ts_SGsAP_UL_UD(imsi, l3_enc));
318 SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
319 }
320 [] SGsAP_CLIENT.receive(PDU_ML3_NW_MS:?) -> value l3_mt sender vc_conn {
321 var octetstring l3_enc := enc_PDU_ML3_NW_MS(l3_mt);
322 imsi := f_imsi_by_comp(vc_conn);
323 msg := valueof(ts_SGsAP_DL_UD(imsi, l3_enc));
324 SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, msg));
325 }
326
327 /* DTAP/MobileL3 from MSC/VLR to MS wrapped in SGsAP-DL-UD: Extract, decode, forward */
328 [] SGsAP.receive(tr_SGsAP_RecvFrom_R(tr_SGsAP_DL_UD(?,?))) -> value mrf {
329 var octetstring l3_enc := mrf.msg.sGsAP_DOWNLINK_UNITDATA.nAS_MessageContainer.nAS_MessageContainer;
330 imsi := mrf.msg.sGsAP_DOWNLINK_UNITDATA.iMSI.iMSI.digits;
331 vc_conn := f_comp_by_imsi(imsi);
332 l3_mt := dec_PDU_ML3_NW_MS(l3_enc);
333 SGsAP_CLIENT.send(l3_mt) to vc_conn;
334 }
335 [] SGsAP.receive(tr_SGsAP_RecvFrom_R(tr_SGsAP_UL_UD(?,?))) -> value mrf {
336 var octetstring l3_enc := mrf.msg.sGsAP_UPLINK_UNITDATA.nAS_MessageContainer.nAS_MessageContainer;
337 imsi := mrf.msg.sGsAP_UPLINK_UNITDATA.iMSI.iMSI.digits;
338 l3_mo := dec_PDU_ML3_MS_NW(l3_enc);
339 vc_conn := f_comp_by_imsi(imsi);
340 SGsAP_CLIENT.send(l3_mo) to vc_conn;
341 }
342
343
344 /* any other SGsAP from MSC/VLR */
Harald Welteaf5bce42018-10-09 09:20:45 +0200345 [] SGsAP.receive(tr_SGsAP_RecvFrom_R(?)) -> value mrf {
346 imsi_t := f_SGsAP_get_imsi(mrf.msg);
347 if (isvalue(imsi_t)) {
348 imsi := valueof(imsi_t.iMSI.digits);
349 if (f_imsi_known(imsi)) {
350 vc_conn := f_comp_by_imsi(imsi);
351 SGsAP_CLIENT.send(mrf.msg) to vc_conn;
352 } else {
353 vc_conn := ops.create_cb.apply(mrf.msg, imsi, id);
354 f_imsi_table_add(vc_conn, imsi);
355 SGsAP_CLIENT.send(mrf.msg) to vc_conn;
356 }
357 } else {
358 /* message contained no IMSI; is not IMSI-oriented */
359 var template PDU_SGsAP resp := ops.unitdata_cb.apply(mrf.msg);
360 if (isvalue(resp)) {
361 SGsAP.send(t_SGsAP_Send(g_sgsap_conn_id, valueof(resp)));
362 }
363 }
364 }
365 [] SGsAP.receive(tr_SctpAssocChange) { }
366 [] SGsAP.receive(tr_SctpPeerAddrChange) { }
367 [] SGsAP_PROC.getcall(SGsAPEM_register:{?,?}) -> param(imsi, vc_conn) {
368 f_create_expect(imsi, vc_conn);
369 SGsAP_PROC.reply(SGsAPEM_register:{imsi, vc_conn}) to vc_conn;
370 }
Harald Welte9a5dd542018-10-28 10:33:23 +0100371 [] SGsAP_PROC.getcall(SGsAPEM_reset_mme:{?,-}) -> param(mme_name) {
372 var octetstring mme_enc, vlr_enc;
373 mme_enc := f_enc_dns_hostname(mme_name);
374 msg := f_sgsap_xceive(ts_SGsAP_RESET_IND_MME(mme_enc));
375 vlr_enc := msg.sGsAP_RESET_ACK.vLR_Name.name;
376 vlr_name := f_dec_dns_hostname(vlr_enc);
377 SGsAP_PROC.reply(SGsAPEM_reset_mme:{mme_name, vlr_name});
378 }
379 [] SGsAP_PROC.getcall(SGsAPEM_reset_vlr:{?,-}) -> param(vlr_name) {
380 var octetstring mme_enc, vlr_enc;
381 vlr_enc := f_enc_dns_hostname(vlr_name);
382 msg := f_sgsap_xceive(ts_SGsAP_RESET_IND_VLR(vlr_enc));
383 mme_enc := msg.sGsAP_RESET_ACK.mME_Name.name;
384 mme_name := f_dec_dns_hostname(mme_enc);
385 SGsAP_PROC.reply(SGsAPEM_reset_vlr:{vlr_name, mme_name});
386 }
387
388
Harald Welteaf5bce42018-10-09 09:20:45 +0200389 }
390
391 }
392}
393
394/* "Expect" Handling */
395
396type record ExpectData {
397 hexstring imsi optional,
398 SGsAP_ConnHdlr vc_conn
399}
400
401signature SGsAPEM_register(in hexstring imsi, in SGsAP_ConnHdlr hdlr);
402
Harald Welte9a5dd542018-10-28 10:33:23 +0100403signature SGsAPEM_reset_vlr(in charstring vlr_name, out charstring mme_name);
404signature SGsAPEM_reset_mme(in charstring mme_name, out charstring vlr_name);
405
Harald Welteaf5bce42018-10-09 09:20:45 +0200406type port SGsAPEM_PROC_PT procedure {
Harald Welte9a5dd542018-10-28 10:33:23 +0100407 inout SGsAPEM_register, SGsAPEM_reset_vlr, SGsAPEM_reset_mme;
Harald Welteaf5bce42018-10-09 09:20:45 +0200408} with { extension "internal" };
409
410/* Function that can be used as create_cb and will usse the expect table */
411function ExpectedCreateCallback(PDU_SGsAP msg, hexstring imsi, charstring id)
412runs on SGsAP_Emulation_CT return SGsAP_ConnHdlr {
413 var SGsAP_ConnHdlr ret := null;
414 var integer i;
415
416 for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
417 if (not ispresent(SgsapExpectTable[i].imsi)) {
418 continue;
419 }
420 if (imsi == SgsapExpectTable[i].imsi) {
421 ret := SgsapExpectTable[i].vc_conn;
422 /* Release this entry */
423 SgsapExpectTable[i].imsi := omit;
424 SgsapExpectTable[i].vc_conn := null;
425 log("Found Expect[", i, "] for ", msg, " handled at ", ret);
426 return ret;
427 }
428 }
429 setverdict(fail, "Couldn't find Expect for ", msg);
430 mtc.stop;
431}
432
433private function f_create_expect(hexstring imsi, SGsAP_ConnHdlr hdlr)
434runs on SGsAP_Emulation_CT {
435 var integer i;
436
437 /* Check an entry like this is not already presnt */
438 for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
439 if (imsi == SgsapExpectTable[i].imsi) {
440 setverdict(fail, "IMSI already present", imsi);
441 mtc.stop;
442 }
443 }
444 for (i := 0; i < sizeof(SgsapExpectTable); i := i+1) {
445 if (not ispresent(SgsapExpectTable[i].imsi)) {
446 SgsapExpectTable[i].imsi := imsi;
447 SgsapExpectTable[i].vc_conn := hdlr;
448 log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
449 return;
450 }
451 }
452 testcase.stop("No space left in SgsapExpectTable")
453}
454
455/* client/conn_hdlr side function to use procedure port to create expect in emulation */
456function f_create_sgsap_expect(hexstring imsi) runs on SGsAP_ConnHdlr {
457 SGsAP_PROC.call(SGsAPEM_register:{imsi, self}) {
458 [] SGsAP_PROC.getreply(SGsAPEM_register:{?,?}) {};
459 }
460}
461
Harald Welte9a5dd542018-10-28 10:33:23 +0100462/* client/conn_hdlr side function to use procedure port to send RESET from emulated MME */
463function f_sgsap_reset_mme(charstring mme_name) runs on SGsAP_ConnHdlr return charstring {
464 var charstring vlr_name;
465 SGsAP_PROC.call(SGsAPEM_reset_mme:{mme_name, -}) {
466 [] SGsAP_PROC.getreply(SGsAPEM_reset_mme:{mme_name,?}) -> param(vlr_name) {
467 return vlr_name;
468 };
469 }
470}
471
472/* client/conn_hdlr side function to use procedure port to send RESET from emulated VLR */
473function f_sgsap_reset_vlr(charstring vlr_name) runs on SGsAP_ConnHdlr return charstring {
474 var charstring mme_name;
475 SGsAP_PROC.call(SGsAPEM_reset_vlr:{vlr_name, -}) {
476 [] SGsAP_PROC.getreply(SGsAPEM_reset_vlr:{vlr_name,?}) -> param(mme_name) {
477 return mme_name;
478 };
479 }
480}
Harald Welteaf5bce42018-10-09 09:20:45 +0200481
482private function f_expect_table_init()
483runs on SGsAP_Emulation_CT {
484 var integer i;
485 for (i := 0; i < sizeof(SgsapExpectTable); i := i + 1) {
486 SgsapExpectTable[i].imsi := omit;
487 }
488}
489
490function DummyUnitdataCallback(PDU_SGsAP msg)
491runs on SGsAP_Emulation_CT return template PDU_SGsAP {
492 log("Ignoring SGsAP ", msg);
493 return omit;
494}
495
496
497}