blob: 240cce4de369b5bd5cf233f69052bc05e636fbd8 [file] [log] [blame]
Harald Welte88b3ccb2020-03-12 21:36:32 +01001/* GTPv2 Emulation in TTCN-3
2 *
3 * (C) 2018-2020 Harald Welte <laforge@gnumonks.org>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12module GTPv2_Emulation {
13
14import from IPL4asp_Types all;
15import from General_Types all;
16import from Osmocom_Types all;
17import from GTPv2_Types all;
18import from GTPv2_Templates all;
19import from GTPv2_CodecPort all;
20import from GTPv2_CodecPort_CtrlFunct all;
21
22import from UECUPS_Types all;
23import from UECUPS_CodecPort all;
24import from UECUPS_CodecPort_CtrlFunct all;
25
26/***********************************************************************
27 * Main Emulation Component
28 ***********************************************************************/
29
30modulepar {
31 charstring mp_uecups_host := "127.0.0.1";
32 integer mp_uecups_port := UECUPS_SCTP_PORT;
33};
34
35const integer GTP2C_PORT := 2123;
36const integer GTP1U_PORT := 2152;
37
38type record Gtp2EmulationCfg {
39 HostName gtpc_bind_ip,
40 IPL4asp_Types.PortNumber gtpc_bind_port,
41 HostName gtpc_remote_ip,
42 IPL4asp_Types.PortNumber gtpc_remote_port,
43 //HostName gtpu_bind_ip,
44 //PortNumber gtpu_bind_port,
45 boolean sgw_role,
46 boolean use_gtpu_daemon
47};
48
49type component GTPv2_Emulation_CT {
50 /* Communication with underlying GTP CodecPort */
51 port GTPv2C_PT GTP2C;
52
53 /* Control port to GTP-U Daemon */
54 port UECUPS_CODEC_PT UECUPS;
55
56 /* Communication with Clients */
57 port GTP2EM_PT TEID0;
58 port GTP2EM_PT CLIENT;
59 port GTP2EM_PROC_PT CLIENT_PROC;
60
61 /* Configuration by the user */
62 var Gtp2EmulationCfg g_gtp2_cfg;
63
64 /* State */
65 var GtpPeer g_peer;
66 var integer g_gtp2c_id;
67 var OCT1 g_restart_ctr;
68 var uint16_t g_c_seq_nr;
69 var TidTableRec TidTable[256];
70 var SeqTableRec SeqTable[256];
71 var ImsiTableRec ImsiTable[256];
Philipp Maierac791562023-09-01 17:10:24 +020072 var UdMsgTableRec UdMsgTable[256];
Harald Welte88b3ccb2020-03-12 21:36:32 +010073 var PidTableRec PidTable[256];
Philipp Maierac791562023-09-01 17:10:24 +020074
Harald Welte88b3ccb2020-03-12 21:36:32 +010075 var integer g_uecups_conn_id;
76};
77
78/* local TEID <-> ConnHdlr mapping */
79type record TidTableRec {
80 OCT4 teid,
81 GTP2_ConnHdlr vc_conn
82};
83
84/* local SeqNr <-> ConnHdlr mapping (until a response is received */
85type record SeqTableRec {
86 OCT3 seq,
87 GTP2_ConnHdlr vc_conn
88};
89
90/* IMSI <-> ConnHdlr mapping */
91type record ImsiTableRec {
92 hexstring imsi,
93 GTP2_ConnHdlr vc_conn
94};
95
Philipp Maierac791562023-09-01 17:10:24 +020096/* Unit data message type <-> ConnHdlr mapping */
97type record UdMsgTableRec {
98 OCT1 messageType,
99 GTP2_ConnHdlr vc_conn
100};
101
Harald Welte88b3ccb2020-03-12 21:36:32 +0100102/* pid <-> ConnHdlr mapping (for UECUPS process termination indication) */
103type record PidTableRec {
104 /* process ID of the running process */
105 integer pid,
106 /* component that started it */
107 GTP2_ConnHdlr vc_conn
108};
109
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100110private function f_teid_known(OCT4 teid) runs on GTPv2_Emulation_CT return boolean {
111 var integer i;
112 for (i := 0; i < sizeof(TidTable); i := i+1) {
113 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
114 return true;
115 }
116 }
117 return false;
118}
119
Harald Welte88b3ccb2020-03-12 21:36:32 +0100120private function f_comp_by_teid(OCT4 teid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
121 var integer i;
122 for (i := 0; i < sizeof(TidTable); i := i+1) {
123 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
124 return TidTable[i].vc_conn;
125 }
126 }
127 setverdict(fail, "No Component for TEID ", teid);
128 mtc.stop;
129}
130
131private function f_seq_known(OCT3 seq) runs on GTPv2_Emulation_CT return boolean {
132 var integer i;
133 for (i := 0; i < sizeof(SeqTable); i := i+1) {
134 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
135 return true;
136 }
137 }
138 return false;
139}
140
141private function f_comp_by_seq(OCT3 seq) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
142 var integer i;
143 for (i := 0; i < sizeof(SeqTable); i := i+1) {
144 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
145 return SeqTable[i].vc_conn;
146 }
147 }
148 setverdict(fail, "No Component for SEQ ", seq);
149 mtc.stop;
150}
151
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100152private function f_imsi_known(hexstring imsi) runs on GTPv2_Emulation_CT return boolean {
153 var integer i;
154 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
155 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
156 return true;
157 }
158 }
159 return false;
160}
161
Harald Welte88b3ccb2020-03-12 21:36:32 +0100162private function f_comp_by_imsi(hexstring imsi) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
163 var integer i;
164 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
165 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
166 return ImsiTable[i].vc_conn;
167 }
168 }
169 setverdict(fail, "No Component for IMSI ", imsi);
170 mtc.stop;
171}
172
173private function f_comp_by_pid(integer pid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
174 var integer i;
175 for (i := 0; i < sizeof(PidTable); i := i+1) {
176 if (isbound(PidTable[i].pid) and PidTable[i].pid == pid) {
177 /* fixme: remove */
178 return PidTable[i].vc_conn;
179 }
180 }
181 setverdict(fail, "No Component for PID ", pid);
182 mtc.stop;
183}
184
185private function f_tid_tbl_add(OCT4 teid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
186 var integer i;
187 for (i := 0; i < sizeof(TidTable); i := i+1) {
188 if (not isbound(TidTable[i].teid)) {
189 TidTable[i].teid := teid;
190 TidTable[i].vc_conn := vc_conn;
191 return;
192 }
193 }
194 testcase.stop("No Space in TidTable for ", teid);
195}
196
197private function f_seq_tbl_add(OCT3 seq, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
198 var integer i;
199 for (i := 0; i < sizeof(SeqTable); i := i+1) {
200 if (not isbound(SeqTable[i].seq)) {
201 SeqTable[i].seq := seq;
202 SeqTable[i].vc_conn := vc_conn;
203 return;
204 }
205 }
206 testcase.stop("No Space in SeqTable for ", seq);
207}
208
209private function f_seq_tbl_del(OCT3 seq) runs on GTPv2_Emulation_CT {
210 var integer i;
211 for (i := 0; i < sizeof(SeqTable); i := i+1) {
212 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
213 SeqTable[i] := {
214 seq := -,
215 vc_conn := null
216 }
217 }
218 }
219}
220
221private function f_imsi_tbl_add(hexstring imsi, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
222 var integer i;
223 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
224 if (not isbound(ImsiTable[i].imsi)) {
225 ImsiTable[i].imsi := imsi;
226 ImsiTable[i].vc_conn := vc_conn;
227 return;
228 }
229 }
230 testcase.stop("No Space in IMSI Table for ", imsi);
231}
232
Philipp Maierac791562023-09-01 17:10:24 +0200233private function f_udmsg_tbl_add(OCT1 messageType, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
234 var integer i;
235 for (i := 0; i < sizeof(UdMsgTable); i := i+1) {
236 if (not isbound(UdMsgTable[i].messageType)) {
237 UdMsgTable[i].messageType := messageType;
238 UdMsgTable[i].vc_conn := vc_conn;
239 return;
240 }
241 }
242 testcase.stop("No Space in UdMsg Table for messateType ", messageType);
243}
244
Harald Welte88b3ccb2020-03-12 21:36:32 +0100245private function f_pid_tbl_add(integer pid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
246 var integer i;
247 for (i := 0; i < sizeof(PidTable); i := i+1) {
248 if (not isbound(PidTable[i].pid)) {
249 PidTable[i].pid := pid;
250 PidTable[i].vc_conn := vc_conn;
251 return;
252 }
253 }
254 testcase.stop("No Space in PID Table for ", pid);
255}
256
257
258/* allocate an unused local teid */
259private function f_alloc_teid() runs on GTPv2_Emulation_CT return OCT4 {
260 var OCT4 teid;
261 var integer i, j;
262 for (i := 0; i < 100; i := i+1) {
263 teid := f_rnd_octstring(4);
264 for (j := 0; j < sizeof(TidTable); j := j+1) {
265 if (isbound(TidTable) and TidTable[i].teid == teid) {
266 continue;
267 }
268 }
269 /* we iterated over all entries and found no match: great! */
270 return teid;
271 }
272 testcase.stop("Cannot find unused TEID after ", i, " attempts");
273}
274
275/* obtain the IMSI from a GTPv2C PDU, if there is any IMSI contained. The way how the TITAN
276 * GTPv2 decoders are structured (explict IE members rather than a list/set of generic IE structures)
277 * doesn't make this easy, but requires lots of boilerplate code. Oh well.. */
278function f_gtp2c_extract_imsi(PDU_GTPCv2 gtp) return template (omit) hexstring {
279 if (ischosen(gtp.gtpcv2_pdu.createSessionRequest)) {
280 if (ispresent(gtp.gtpcv2_pdu.createSessionRequest.iMSI)) {
281 return gtp.gtpcv2_pdu.createSessionRequest.iMSI.iMSI_Value;
282 }
283 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotification)) {
284 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotification.iMSI)) {
285 return gtp.gtpcv2_pdu.downlinkDataNotification.iMSI.iMSI_Value;
286 }
287 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement)) {
288 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI)) {
289 return gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI.iMSI_Value;
290 }
291 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication)) {
292 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI)) {
293 return gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI.iMSI_Value;
294 }
295 } else if (ischosen(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest)) {
296 if (ispresent(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI)) {
297 return gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI.iMSI_Value;
298 }
299 } else if (ischosen(gtp.gtpcv2_pdu.stopPagingIndication)) {
300 if (ispresent(gtp.gtpcv2_pdu.stopPagingIndication.iMSI)) {
301 return gtp.gtpcv2_pdu.stopPagingIndication.iMSI.iMSI_Value;
302 }
303 } else if (ischosen(gtp.gtpcv2_pdu.forwardRelocationRequest)) {
304 if (ispresent(gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI)) {
305 return gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI.iMSI_Value;
306 }
307 } else if (ischosen(gtp.gtpcv2_pdu.contextRequest)) {
308 if (ispresent(gtp.gtpcv2_pdu.contextRequest.iMSI)) {
309 return gtp.gtpcv2_pdu.contextRequest.iMSI.iMSI_Value;
310 }
311 } else if (ischosen(gtp.gtpcv2_pdu.identificationResponse)) {
312 if (ispresent(gtp.gtpcv2_pdu.identificationResponse.iMSI)) {
313 return gtp.gtpcv2_pdu.identificationResponse.iMSI.iMSI_Value;
314 }
315 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationRequest)) {
316 if (ispresent(gtp.gtpcv2_pdu.changeNotificationRequest)) {
317 return gtp.gtpcv2_pdu.changeNotificationRequest.iMSI.iMSI_Value;
318 }
319 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationResponse)) {
320 if (ispresent(gtp.gtpcv2_pdu.changeNotificationResponse.iMSI)) {
321 return gtp.gtpcv2_pdu.changeNotificationResponse.iMSI.iMSI_Value;
322 }
323 } else if (ischosen(gtp.gtpcv2_pdu.relocationCancelRequest)) {
324 if (ispresent(gtp.gtpcv2_pdu.relocationCancelRequest.iMSI)) {
325 return gtp.gtpcv2_pdu.relocationCancelRequest.iMSI.iMSI_Value;
326 }
327 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
328 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI)) {
329 return gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI.iMSI_Value;
330 }
331 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse)) {
332 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI)) {
333 return gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI.iMSI_Value;
334 }
335 } else if (ischosen(gtp.gtpcv2_pdu.suspendNotification)) {
336 if (ispresent(gtp.gtpcv2_pdu.suspendNotification.iMSI)) {
337 return gtp.gtpcv2_pdu.suspendNotification.iMSI.iMSI_Value;
338 }
339 } else if (ischosen(gtp.gtpcv2_pdu.resumeNotification)) {
340 if (ispresent(gtp.gtpcv2_pdu.resumeNotification.iMSI)) {
341 return gtp.gtpcv2_pdu.resumeNotification.iMSI.iMSI_Value;
342 }
343 } else if (ischosen(gtp.gtpcv2_pdu.cSPagingIndication)) {
344 if (ispresent(gtp.gtpcv2_pdu.cSPagingIndication.iMSI)) {
345 return gtp.gtpcv2_pdu.cSPagingIndication.iMSI.iMSI_Value;
346 }
347 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification)) {
348 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI)) {
349 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI.iMSI_Value;
350 }
351 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge)) {
352 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI)) {
353 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI.iMSI_Value;
354 }
355 } else if (ischosen(gtp.gtpcv2_pdu.traceSessionActivation)) {
356 if (ispresent(gtp.gtpcv2_pdu.traceSessionActivation.iMSI)) {
357 return gtp.gtpcv2_pdu.traceSessionActivation.iMSI.iMSI_Value;
358 }
359 }
360 return omit;
361}
362
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100363private function f_gtp2c_is_initial_msg(PDU_GTPCv2 msg) return boolean
364{
365 if (ischosen(msg.gtpcv2_pdu.echoRequest) or
366 ischosen(msg.gtpcv2_pdu.versionNotSupported) or
367 ischosen(msg.gtpcv2_pdu.createSessionRequest) or
368 ischosen(msg.gtpcv2_pdu.createBearerRequest) or
369 ischosen(msg.gtpcv2_pdu.bearerResourceCommand) or
370 ischosen(msg.gtpcv2_pdu.bearerResourceFailureIndication) or
371 ischosen(msg.gtpcv2_pdu.modifyBearerRequest) or
372 ischosen(msg.gtpcv2_pdu.deleteSessionRequest) or
373 ischosen(msg.gtpcv2_pdu.deleteBearerRequest) or
374 ischosen(msg.gtpcv2_pdu.downlinkDataNotification) or
375 ischosen(msg.gtpcv2_pdu.downlinkDataNotificationAcknowledgement) or
376 ischosen(msg.gtpcv2_pdu.downlinkDataNotificationFailureIndication) or
377 ischosen(msg.gtpcv2_pdu.deleteIndirectDataForwardingTunnelRequest) or
378 ischosen(msg.gtpcv2_pdu.modifyBearerCommand) or
379 ischosen(msg.gtpcv2_pdu.modifyBearerFailureIndication) or
380 ischosen(msg.gtpcv2_pdu.updateBearerRequest) or
381 ischosen(msg.gtpcv2_pdu.deleteBearerCommand) or
382 ischosen(msg.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest) or
383 ischosen(msg.gtpcv2_pdu.releaseAccessBearersRequest) or
384 ischosen(msg.gtpcv2_pdu.stopPagingIndication) or
385 ischosen(msg.gtpcv2_pdu.modifyAccessBearersRequest) or
386 ischosen(msg.gtpcv2_pdu.remoteUEReportNotification) or
387 ischosen(msg.gtpcv2_pdu.remoteUEReportAcknowledge) or
388 ischosen(msg.gtpcv2_pdu.forwardRelocationRequest) or
389 ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteNotification) or
390 ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteAcknowledge) or
391 ischosen(msg.gtpcv2_pdu.contextRequest) or
392 ischosen(msg.gtpcv2_pdu.contextAcknowledge) or
393 ischosen(msg.gtpcv2_pdu.identificationRequest) or
394 ischosen(msg.gtpcv2_pdu.forwardAccessContextNotification) or
395 ischosen(msg.gtpcv2_pdu.forwardAccessContextAcknowledge) or
396 ischosen(msg.gtpcv2_pdu.detachNotification) or
397 ischosen(msg.gtpcv2_pdu.detachAcknowledge) or
398 ischosen(msg.gtpcv2_pdu.changeNotificationRequest) or
399 ischosen(msg.gtpcv2_pdu.relocationCancelRequest) or
400 ischosen(msg.gtpcv2_pdu.configurationTransferTunnel) or
401 ischosen(msg.gtpcv2_pdu.rAN_InformationRelay) or
402 ischosen(msg.gtpcv2_pdu.suspendNotification) or
403 ischosen(msg.gtpcv2_pdu.suspendAcknowledge) or
404 ischosen(msg.gtpcv2_pdu.resumeNotification) or
405 ischosen(msg.gtpcv2_pdu.resumeAcknowledge) or
406 ischosen(msg.gtpcv2_pdu.cSPagingIndication) or
407 ischosen(msg.gtpcv2_pdu.createForwardingTunnelRequest) or
408 ischosen(msg.gtpcv2_pdu.deletePDN_ConnectionSetRequest) or
409 ischosen(msg.gtpcv2_pdu.traceSessionActivation) or
410 ischosen(msg.gtpcv2_pdu.traceSessionDeactivation) or
411 ischosen(msg.gtpcv2_pdu.updatePDN_ConnectionSetRequest) or
412 ischosen(msg.gtpcv2_pdu.pGW_RestartNotification) or
413 ischosen(msg.gtpcv2_pdu.pGW_RestartNotificationAcknowledge) or
414 ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringNotification) or
415 ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge) or
416 ischosen(msg.gtpcv2_pdu.alertMMENotification) or
417 ischosen(msg.gtpcv2_pdu.alertMMEAcknowledge) or
418 ischosen(msg.gtpcv2_pdu.uEActivityNotification) or
419 ischosen(msg.gtpcv2_pdu.uEActivityAcknowledge) or
420 ischosen(msg.gtpcv2_pdu.mBMSSessionStartRequest) or
421 ischosen(msg.gtpcv2_pdu.mBMSSessionUpdateRequest) or
422 ischosen(msg.gtpcv2_pdu.mBMSSessionStopRequest) or
423 ischosen(msg.gtpcv2_pdu.iSR_StatusIndication) or
424 ischosen(msg.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
425 return true;
426 }
427 return false;
428}
429
Harald Welte88b3ccb2020-03-12 21:36:32 +0100430private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
431 sinfo_stream := omit,
432 sinfo_ppid := ppid,
433 remSocks := omit,
434 assocId := omit
435};
436
437function tr_UECUPS_RecvFrom_R(template PDU_UECUPS msg)
438runs on GTPv2_Emulation_CT return template UECUPS_RecvFrom {
439 var template UECUPS_RecvFrom mrf := {
440 connId := g_uecups_conn_id,
441 remName := ?,
442 remPort := ?,
443 locName := ?,
444 locPort := ?,
445 msg := msg
446 }
447 return mrf;
448}
449
450
451private template PortEvent tr_SctpAssocChange := {
452 sctpEvent := {
453 sctpAssocChange := ?
454 }
455}
456private template PortEvent tr_SctpPeerAddrChange := {
457 sctpEvent := {
458 sctpPeerAddrChange := ?
459 }
460}
461
462private function f_uecups_xceive(template (value) PDU_UECUPS tx,
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200463 template PDU_UECUPS rx_t := ?, float time_out := 10.0)
Harald Welte88b3ccb2020-03-12 21:36:32 +0100464runs on GTPv2_Emulation_CT return PDU_UECUPS {
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200465 timer T := time_out;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100466 var UECUPS_RecvFrom mrf;
467
468 UECUPS.send(t_UECUPS_Send(g_uecups_conn_id, tx));
Vadim Yanitskiy5313af92022-01-20 18:53:15 +0600469 T.start;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100470 alt {
471 [] UECUPS.receive(tr_UECUPS_RecvFrom_R(rx_t)) -> value mrf { }
472 [] UECUPS.receive(tr_SctpAssocChange) { repeat; }
473 [] UECUPS.receive(tr_SctpPeerAddrChange) { repeat; }
474 [] T.timeout {
475 setverdict(fail, "Timeout waiting for ", rx_t);
476 mtc.stop;
477 }
478 }
479 return mrf.msg;
480}
481
482private function f_init(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
483 var Result res;
484
485 map(self:GTP2C, system:GTP2C);
486 res := GTPv2_CodecPort_CtrlFunct.f_IPL4_listen(GTP2C, cfg.gtpc_bind_ip,
487 cfg.gtpc_bind_port, {udp:={}});
488 g_gtp2c_id := res.connId;
489
490 g_restart_ctr := f_rnd_octstring(1);
491 g_c_seq_nr := f_rnd_int(65535);
492 g_gtp2_cfg := cfg;
493 g_peer := {
494 connId := g_gtp2c_id,
495 remName := g_gtp2_cfg.gtpc_remote_ip,
496 remPort := g_gtp2_cfg.gtpc_remote_port
497 }
498
Philipp Maierb11ee502023-08-31 16:48:19 +0200499 g_uecups_conn_id := res.connId;
500
Harald Welte88b3ccb2020-03-12 21:36:32 +0100501 if (g_gtp2_cfg.use_gtpu_daemon) {
502 map(self:UECUPS, system:UECUPS);
503 res := UECUPS_CodecPort_CtrlFunct.f_IPL4_connect(UECUPS, mp_uecups_host, mp_uecups_port, "", -1, -1, { sctp := valueof(ts_SCTP) });
504 if (not ispresent(res.connId)) {
505 setverdict(fail, "Could not connect UECUPS socket, check your configuration");
506 testcase.stop;
507 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100508
509 /* clear all tunnel state in the daemon at start */
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200510 f_uecups_xceive({reset_all_state := {}}, {reset_all_state_res:=?}, 30.0);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100511 }
512
513 /* make sure we always pass incoming UECUPS indications whenever receiving fom the UECUPS port */
514 activate(as_uecups_ind());
515}
516
517private altstep as_uecups_ind() runs on GTPv2_Emulation_CT {
518var UECUPS_RecvFrom rx;
519var GTP2_ConnHdlr vc_conn;
520/* handle incoming program_term_ind; dispatch to whatever component started the process */
521[] UECUPS.receive(tr_UECUPS_RecvFrom_R({program_term_ind:=?})) -> value rx {
522 vc_conn := f_comp_by_pid(rx.msg.program_term_ind.pid);
523 CLIENT.send(rx.msg.program_term_ind) to vc_conn;
524 /* FIXME: remove from table */
525 repeat;
526 }
527}
528
Philipp Maierac791562023-09-01 17:10:24 +0200529private function SendToUdMsgTable(Gtp2cUnitdata g2c_ud) runs on GTPv2_Emulation_CT {
530 var GTP2_ConnHdlr vc_conn;
531
532 for (var integer i := 0; i < sizeof(UdMsgTable); i := i + 1) {
533 if (isbound(UdMsgTable[i].messageType)) {
534 if (UdMsgTable[i].messageType == g2c_ud.gtpc.messageType) {
535 vc_conn := UdMsgTable[i].vc_conn;
536 CLIENT.send(g2c_ud.gtpc) to vc_conn;
537 }
538 }
539 }
540
541 return;
542}
543
Harald Welte88b3ccb2020-03-12 21:36:32 +0100544function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
545 var Gtp2cUnitdata g2c_ud;
546 var PDU_GTPCv2 g2c;
547 var GTP2_ConnHdlr vc_conn;
548 var hexstring imsi;
Philipp Maierac791562023-09-01 17:10:24 +0200549 var OCT1 messageType;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100550 var OCT4 teid;
551 var PDU_UECUPS rx_uecups;
552 var UECUPS_CreateTun gtc;
553 var UECUPS_DestroyTun gtd;
554 var UECUPS_StartProgram sprog;
555
556 f_init(cfg);
557
558 while (true) {
559 alt {
560 /* route inbound GTP2-C based on TEID, SEQ or IMSI */
561 [] GTP2C.receive(Gtp2cUnitdata:?) -> value g2c_ud {
562 var template hexstring imsi_t := f_gtp2c_extract_imsi(g2c_ud.gtpc);
Pau Espin Pedrol167bca42024-01-09 12:14:20 +0100563 /* if this is a response, route by SEQ: */
564 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))
565 and f_seq_known(g2c_ud.gtpc.sequenceNumber)) {
566 vc_conn := f_comp_by_seq(g2c_ud.gtpc.sequenceNumber);
567 CLIENT.send(g2c_ud.gtpc) to vc_conn;
568 }else if (isvalue(imsi_t) and f_imsi_known(valueof(imsi_t))) {
Harald Welte88b3ccb2020-03-12 21:36:32 +0100569 vc_conn := f_comp_by_imsi(valueof(imsi_t));
570 CLIENT.send(g2c_ud.gtpc) to vc_conn;
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100571 } else if ((ispresent(g2c_ud.gtpc.tEID) and g2c_ud.gtpc.tEID != '00000000'O)
572 and f_teid_known(g2c_ud.gtpc.tEID)) {
573 vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
574 CLIENT.send(g2c_ud.gtpc) to vc_conn;
575 } else if ((not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O)
576 and f_teid_known('00000000'O)) {
577 vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
578 CLIENT.send(g2c_ud.gtpc) to vc_conn;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100579 } else {
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100580 SendToUdMsgTable(g2c_ud);
Pau Espin Pedrol167bca42024-01-09 12:14:20 +0100581 if (not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O) {
582 TEID0.send(g2c_ud.gtpc);
583 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100584 }
585
586 /* remove sequence number if response was received */
587 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))) {
588 f_seq_tbl_del(g2c_ud.gtpc.sequenceNumber);
589 }
590
591 }
592
593 [] TEID0.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100594 /* patch in the next sequence number on outbound Initial message */
595 if (f_gtp2c_is_initial_msg(g2c)) {
596 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
597 g_c_seq_nr := g_c_seq_nr + 1;
598 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100599 /* build Gtp2cUnitdata */
600 g2c_ud := { peer := g_peer, gtpc := g2c };
601 GTP2C.send(g2c_ud);
602 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
603 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
604 }
605 }
606
607 [] CLIENT.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100608 /* patch in the next sequence number on outbound Initial message */
609 if (f_gtp2c_is_initial_msg(g2c)) {
610 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
611 g_c_seq_nr := g_c_seq_nr + 1;
612 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100613 /* build Gtp2cUnitdata */
614 g2c_ud := { peer := g_peer, gtpc := g2c };
615 GTP2C.send(g2c_ud);
616 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
617 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
618 }
619 }
620
621 [] CLIENT_PROC.getcall(GTP2EM_register_imsi:{?}) -> param(imsi) sender vc_conn {
622 f_imsi_tbl_add(imsi, vc_conn);
623 CLIENT_PROC.reply(GTP2EM_register_imsi:{imsi}) to vc_conn;
624 }
Philipp Maierac791562023-09-01 17:10:24 +0200625 [] CLIENT_PROC.getcall(GTP2EM_register_udmsg:{?}) -> param(messageType) sender vc_conn {
626 f_udmsg_tbl_add(messageType, vc_conn);
627 CLIENT_PROC.reply(GTP2EM_register_udmsg:{messageType}) to vc_conn;
628 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100629
630 [] CLIENT_PROC.getcall(GTP2EM_register_teid:{?}) -> param(teid) sender vc_conn {
631 f_tid_tbl_add(teid, vc_conn);
632 CLIENT_PROC.reply(GTP2EM_register_teid:{teid}) to vc_conn;
633 }
634 [] CLIENT_PROC.getcall(GTP2EM_allocate_teid:{}) -> sender vc_conn {
635 var OCT4 t := f_alloc_teid();
636 f_tid_tbl_add(t, vc_conn);
637 CLIENT_PROC.reply(GTP2EM_allocate_teid:{} value t) to vc_conn;
638 }
639 [] CLIENT_PROC.getcall(GTP2EM_create_tunnel:{?}) -> param(gtc) sender vc_conn {
640 rx_uecups := f_uecups_xceive({create_tun := gtc}, {create_tun_res:={result:=OK}});
641 CLIENT_PROC.reply(GTP2EM_create_tunnel:{gtc}) to vc_conn;
642 }
643 [] CLIENT_PROC.getcall(GTP2EM_destroy_tunnel:{?}) -> param(gtd) sender vc_conn {
644 rx_uecups := f_uecups_xceive({destroy_tun := gtd}, {destroy_tun_res:={result:=OK}});
645 CLIENT_PROC.reply(GTP2EM_destroy_tunnel:{gtd}) to vc_conn;
646 }
647 [] CLIENT_PROC.getcall(GTP2EM_start_program:{?}) -> param(sprog) sender vc_conn {
648 rx_uecups := f_uecups_xceive({start_program := sprog}, {start_program_res:=?});
649 /* if successful: store (pid, vc_conn) tuple so we can route program_term_ind */
650 if (rx_uecups.start_program_res.result == OK) {
651 f_pid_tbl_add(rx_uecups.start_program_res.pid, vc_conn);
652 }
653 CLIENT_PROC.reply(GTP2EM_start_program:{sprog} value rx_uecups.start_program_res) to vc_conn;
654 }
655
656 }
657 }
658}
659
660
661/***********************************************************************
662 * Interaction between Main and Client Components
663 ***********************************************************************/
664type port GTP2EM_PT message {
665 inout PDU_GTPCv2, UECUPS_ProgramTermInd;
666} with { extension "internal" };
667
668signature GTP2EM_register_imsi(hexstring imsi);
Philipp Maierac791562023-09-01 17:10:24 +0200669signature GTP2EM_register_udmsg(OCT1 messageType);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100670signature GTP2EM_register_teid(OCT4 teid);
671signature GTP2EM_allocate_teid() return OCT4;
672signature GTP2EM_create_tunnel(UECUPS_CreateTun gtc);
673signature GTP2EM_destroy_tunnel(UECUPS_DestroyTun gtd);
674signature GTP2EM_start_program(UECUPS_StartProgram sprog) return UECUPS_StartProgramRes;
675
676type port GTP2EM_PROC_PT procedure {
Philipp Maierac791562023-09-01 17:10:24 +0200677 inout GTP2EM_register_imsi, GTP2EM_register_udmsg, GTP2EM_register_teid, GTP2EM_allocate_teid,
Harald Welte88b3ccb2020-03-12 21:36:32 +0100678 GTP2EM_create_tunnel, GTP2EM_destroy_tunnel, GTP2EM_start_program;
679} with { extension "internal" };
680
681/***********************************************************************
682 * Client Component
683 ***********************************************************************/
684
685type component GTP2_ConnHdlr {
686 port GTP2EM_PT GTP2;
687 port GTP2EM_PROC_PT GTP2_PROC;
688};
689
690function f_gtp2_register_imsi(hexstring imsi) runs on GTP2_ConnHdlr {
Pau Espin Pedrol5c18a0c2023-10-19 13:41:41 +0200691 /* 15-digit IMSIs are len(imsi)=15, but decoded messages are
692 * octet-aligned, hence the hexstring in messages is len(imsi)=16, where
693 * the last hex char is a padding 'F'H */
694 imsi := f_pad_bcd_number(imsi);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100695 GTP2_PROC.call(GTP2EM_register_imsi:{imsi}) {
696 [] GTP2_PROC.getreply(GTP2EM_register_imsi:{imsi});
697 }
698}
699
Philipp Maierac791562023-09-01 17:10:24 +0200700function f_gtp2_register_udmsg(OCT1 messageType) runs on GTP2_ConnHdlr {
701 GTP2_PROC.call(GTP2EM_register_udmsg:{messageType}) {
702 [] GTP2_PROC.getreply(GTP2EM_register_udmsg:{messageType});
703 }
704}
705
Harald Welte88b3ccb2020-03-12 21:36:32 +0100706function f_gtp2_register_teid(OCT4 teid) runs on GTP2_ConnHdlr {
707 GTP2_PROC.call(GTP2EM_register_teid:{teid}) {
708 [] GTP2_PROC.getreply(GTP2EM_register_teid:{teid});
709 }
710}
711
712function f_gtp2_allocate_teid() runs on GTP2_ConnHdlr return OCT4 {
713 var OCT4 t;
714 GTP2_PROC.call(GTP2EM_allocate_teid:{}) {
715 [] GTP2_PROC.getreply(GTP2EM_allocate_teid:{}) -> value t {
716 return t;
717 }
718 }
719}
720
721function f_gtp2_create_tunnel(template (value) UECUPS_CreateTun gtc)
722runs on GTP2_ConnHdlr {
723 GTP2_PROC.call(GTP2EM_create_tunnel:{valueof(gtc)}) {
724 [] GTP2_PROC.getreply(GTP2EM_create_tunnel:{gtc});
725 }
726}
727
728function f_gtp2_destroy_tunnel(template (value) UECUPS_DestroyTun gtd)
729runs on GTP2_ConnHdlr {
730 GTP2_PROC.call(GTP2EM_destroy_tunnel:{valueof(gtd)}) {
731 [] GTP2_PROC.getreply(GTP2EM_destroy_tunnel:{gtd});
732 }
733}
734
735function f_gtp2_start_program(template (value) UECUPS_StartProgram sprog)
736runs on GTP2_ConnHdlr return UECUPS_StartProgramRes {
737 var UECUPS_StartProgramRes res;
738 GTP2_PROC.call(GTP2EM_start_program:{valueof(sprog)}) {
739 [] GTP2_PROC.getreply(GTP2EM_start_program:{sprog}) -> value res;
740 }
741 return res;
742}
743
744
745
746}