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