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