blob: f3f09f807da0b9b8c9e0d4d63b6469b403ef287b [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;
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +010017import from GTPU_Types all;
18import from GTPv1U_CodecPort all;
19import from GTPv1U_CodecPort_CtrlFunct all;
Harald Welte88b3ccb2020-03-12 21:36:32 +010020import from GTPv2_Types all;
21import from GTPv2_Templates all;
22import from GTPv2_CodecPort all;
23import from GTPv2_CodecPort_CtrlFunct all;
24
25import from UECUPS_Types all;
26import from UECUPS_CodecPort all;
27import from UECUPS_CodecPort_CtrlFunct all;
28
29/***********************************************************************
30 * Main Emulation Component
31 ***********************************************************************/
32
33modulepar {
34 charstring mp_uecups_host := "127.0.0.1";
35 integer mp_uecups_port := UECUPS_SCTP_PORT;
36};
37
38const integer GTP2C_PORT := 2123;
39const integer GTP1U_PORT := 2152;
40
41type record Gtp2EmulationCfg {
42 HostName gtpc_bind_ip,
43 IPL4asp_Types.PortNumber gtpc_bind_port,
44 HostName gtpc_remote_ip,
45 IPL4asp_Types.PortNumber gtpc_remote_port,
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +010046 HostName gtpu_bind_ip optional,
47 IPL4asp_Types.PortNumber gtpu_bind_port optional,
Harald Welte88b3ccb2020-03-12 21:36:32 +010048 boolean sgw_role,
49 boolean use_gtpu_daemon
50};
51
52type component GTPv2_Emulation_CT {
53 /* Communication with underlying GTP CodecPort */
54 port GTPv2C_PT GTP2C;
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +010055 port GTPU_PT GTPU;
Harald Welte88b3ccb2020-03-12 21:36:32 +010056
57 /* Control port to GTP-U Daemon */
58 port UECUPS_CODEC_PT UECUPS;
59
60 /* Communication with Clients */
61 port GTP2EM_PT TEID0;
62 port GTP2EM_PT CLIENT;
63 port GTP2EM_PROC_PT CLIENT_PROC;
64
65 /* Configuration by the user */
66 var Gtp2EmulationCfg g_gtp2_cfg;
67
68 /* State */
Pau Espin Pedrol4b090c92024-02-29 19:47:07 +010069 var Gtp2cPeer g_peer;
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +010070 var integer g_gtp2c_id, g_gtp1u_id;
Harald Welte88b3ccb2020-03-12 21:36:32 +010071 var OCT1 g_restart_ctr;
72 var uint16_t g_c_seq_nr;
73 var TidTableRec TidTable[256];
74 var SeqTableRec SeqTable[256];
75 var ImsiTableRec ImsiTable[256];
Philipp Maierac791562023-09-01 17:10:24 +020076 var UdMsgTableRec UdMsgTable[256];
Harald Welte88b3ccb2020-03-12 21:36:32 +010077 var PidTableRec PidTable[256];
Philipp Maierac791562023-09-01 17:10:24 +020078
Harald Welte88b3ccb2020-03-12 21:36:32 +010079 var integer g_uecups_conn_id;
80};
81
82/* local TEID <-> ConnHdlr mapping */
83type record TidTableRec {
84 OCT4 teid,
85 GTP2_ConnHdlr vc_conn
86};
87
88/* local SeqNr <-> ConnHdlr mapping (until a response is received */
89type record SeqTableRec {
90 OCT3 seq,
91 GTP2_ConnHdlr vc_conn
92};
93
94/* IMSI <-> ConnHdlr mapping */
95type record ImsiTableRec {
96 hexstring imsi,
97 GTP2_ConnHdlr vc_conn
98};
99
Philipp Maierac791562023-09-01 17:10:24 +0200100/* Unit data message type <-> ConnHdlr mapping */
101type record UdMsgTableRec {
102 OCT1 messageType,
103 GTP2_ConnHdlr vc_conn
104};
105
Harald Welte88b3ccb2020-03-12 21:36:32 +0100106/* pid <-> ConnHdlr mapping (for UECUPS process termination indication) */
107type record PidTableRec {
108 /* process ID of the running process */
109 integer pid,
110 /* component that started it */
111 GTP2_ConnHdlr vc_conn
112};
113
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100114private function f_teid_known(OCT4 teid) runs on GTPv2_Emulation_CT return boolean {
115 var integer i;
116 for (i := 0; i < sizeof(TidTable); i := i+1) {
117 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
118 return true;
119 }
120 }
121 return false;
122}
123
Harald Welte88b3ccb2020-03-12 21:36:32 +0100124private function f_comp_by_teid(OCT4 teid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
125 var integer i;
126 for (i := 0; i < sizeof(TidTable); i := i+1) {
127 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
128 return TidTable[i].vc_conn;
129 }
130 }
131 setverdict(fail, "No Component for TEID ", teid);
132 mtc.stop;
133}
134
135private function f_seq_known(OCT3 seq) runs on GTPv2_Emulation_CT return boolean {
136 var integer i;
137 for (i := 0; i < sizeof(SeqTable); i := i+1) {
138 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
139 return true;
140 }
141 }
142 return false;
143}
144
145private function f_comp_by_seq(OCT3 seq) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
146 var integer i;
147 for (i := 0; i < sizeof(SeqTable); i := i+1) {
148 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
149 return SeqTable[i].vc_conn;
150 }
151 }
152 setverdict(fail, "No Component for SEQ ", seq);
153 mtc.stop;
154}
155
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100156private function f_imsi_known(hexstring imsi) runs on GTPv2_Emulation_CT return boolean {
157 var integer i;
158 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
159 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
160 return true;
161 }
162 }
163 return false;
164}
165
Harald Welte88b3ccb2020-03-12 21:36:32 +0100166private function f_comp_by_imsi(hexstring imsi) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
167 var integer i;
168 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
169 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
170 return ImsiTable[i].vc_conn;
171 }
172 }
173 setverdict(fail, "No Component for IMSI ", imsi);
174 mtc.stop;
175}
176
177private function f_comp_by_pid(integer pid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
178 var integer i;
179 for (i := 0; i < sizeof(PidTable); i := i+1) {
180 if (isbound(PidTable[i].pid) and PidTable[i].pid == pid) {
181 /* fixme: remove */
182 return PidTable[i].vc_conn;
183 }
184 }
185 setverdict(fail, "No Component for PID ", pid);
186 mtc.stop;
187}
188
189private function f_tid_tbl_add(OCT4 teid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
190 var integer i;
191 for (i := 0; i < sizeof(TidTable); i := i+1) {
192 if (not isbound(TidTable[i].teid)) {
193 TidTable[i].teid := teid;
194 TidTable[i].vc_conn := vc_conn;
195 return;
196 }
197 }
198 testcase.stop("No Space in TidTable for ", teid);
199}
200
201private function f_seq_tbl_add(OCT3 seq, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
202 var integer i;
203 for (i := 0; i < sizeof(SeqTable); i := i+1) {
204 if (not isbound(SeqTable[i].seq)) {
205 SeqTable[i].seq := seq;
206 SeqTable[i].vc_conn := vc_conn;
207 return;
208 }
209 }
210 testcase.stop("No Space in SeqTable for ", seq);
211}
212
213private function f_seq_tbl_del(OCT3 seq) runs on GTPv2_Emulation_CT {
214 var integer i;
215 for (i := 0; i < sizeof(SeqTable); i := i+1) {
216 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
217 SeqTable[i] := {
218 seq := -,
219 vc_conn := null
220 }
221 }
222 }
223}
224
225private function f_imsi_tbl_add(hexstring imsi, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
226 var integer i;
227 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
228 if (not isbound(ImsiTable[i].imsi)) {
229 ImsiTable[i].imsi := imsi;
230 ImsiTable[i].vc_conn := vc_conn;
231 return;
232 }
233 }
234 testcase.stop("No Space in IMSI Table for ", imsi);
235}
236
Philipp Maierac791562023-09-01 17:10:24 +0200237private function f_udmsg_tbl_add(OCT1 messageType, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
238 var integer i;
239 for (i := 0; i < sizeof(UdMsgTable); i := i+1) {
240 if (not isbound(UdMsgTable[i].messageType)) {
241 UdMsgTable[i].messageType := messageType;
242 UdMsgTable[i].vc_conn := vc_conn;
243 return;
244 }
245 }
246 testcase.stop("No Space in UdMsg Table for messateType ", messageType);
247}
248
Harald Welte88b3ccb2020-03-12 21:36:32 +0100249private function f_pid_tbl_add(integer pid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
250 var integer i;
251 for (i := 0; i < sizeof(PidTable); i := i+1) {
252 if (not isbound(PidTable[i].pid)) {
253 PidTable[i].pid := pid;
254 PidTable[i].vc_conn := vc_conn;
255 return;
256 }
257 }
258 testcase.stop("No Space in PID Table for ", pid);
259}
260
261
262/* allocate an unused local teid */
263private function f_alloc_teid() runs on GTPv2_Emulation_CT return OCT4 {
264 var OCT4 teid;
265 var integer i, j;
266 for (i := 0; i < 100; i := i+1) {
267 teid := f_rnd_octstring(4);
268 for (j := 0; j < sizeof(TidTable); j := j+1) {
269 if (isbound(TidTable) and TidTable[i].teid == teid) {
270 continue;
271 }
272 }
273 /* we iterated over all entries and found no match: great! */
274 return teid;
275 }
276 testcase.stop("Cannot find unused TEID after ", i, " attempts");
277}
278
279/* obtain the IMSI from a GTPv2C PDU, if there is any IMSI contained. The way how the TITAN
280 * GTPv2 decoders are structured (explict IE members rather than a list/set of generic IE structures)
281 * doesn't make this easy, but requires lots of boilerplate code. Oh well.. */
282function f_gtp2c_extract_imsi(PDU_GTPCv2 gtp) return template (omit) hexstring {
283 if (ischosen(gtp.gtpcv2_pdu.createSessionRequest)) {
284 if (ispresent(gtp.gtpcv2_pdu.createSessionRequest.iMSI)) {
285 return gtp.gtpcv2_pdu.createSessionRequest.iMSI.iMSI_Value;
286 }
287 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotification)) {
288 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotification.iMSI)) {
289 return gtp.gtpcv2_pdu.downlinkDataNotification.iMSI.iMSI_Value;
290 }
291 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement)) {
292 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI)) {
293 return gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI.iMSI_Value;
294 }
295 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication)) {
296 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI)) {
297 return gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI.iMSI_Value;
298 }
299 } else if (ischosen(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest)) {
300 if (ispresent(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI)) {
301 return gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI.iMSI_Value;
302 }
303 } else if (ischosen(gtp.gtpcv2_pdu.stopPagingIndication)) {
304 if (ispresent(gtp.gtpcv2_pdu.stopPagingIndication.iMSI)) {
305 return gtp.gtpcv2_pdu.stopPagingIndication.iMSI.iMSI_Value;
306 }
307 } else if (ischosen(gtp.gtpcv2_pdu.forwardRelocationRequest)) {
308 if (ispresent(gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI)) {
309 return gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI.iMSI_Value;
310 }
311 } else if (ischosen(gtp.gtpcv2_pdu.contextRequest)) {
312 if (ispresent(gtp.gtpcv2_pdu.contextRequest.iMSI)) {
313 return gtp.gtpcv2_pdu.contextRequest.iMSI.iMSI_Value;
314 }
315 } else if (ischosen(gtp.gtpcv2_pdu.identificationResponse)) {
316 if (ispresent(gtp.gtpcv2_pdu.identificationResponse.iMSI)) {
317 return gtp.gtpcv2_pdu.identificationResponse.iMSI.iMSI_Value;
318 }
319 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationRequest)) {
320 if (ispresent(gtp.gtpcv2_pdu.changeNotificationRequest)) {
321 return gtp.gtpcv2_pdu.changeNotificationRequest.iMSI.iMSI_Value;
322 }
323 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationResponse)) {
324 if (ispresent(gtp.gtpcv2_pdu.changeNotificationResponse.iMSI)) {
325 return gtp.gtpcv2_pdu.changeNotificationResponse.iMSI.iMSI_Value;
326 }
327 } else if (ischosen(gtp.gtpcv2_pdu.relocationCancelRequest)) {
328 if (ispresent(gtp.gtpcv2_pdu.relocationCancelRequest.iMSI)) {
329 return gtp.gtpcv2_pdu.relocationCancelRequest.iMSI.iMSI_Value;
330 }
331 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
332 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI)) {
333 return gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI.iMSI_Value;
334 }
335 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse)) {
336 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI)) {
337 return gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI.iMSI_Value;
338 }
339 } else if (ischosen(gtp.gtpcv2_pdu.suspendNotification)) {
340 if (ispresent(gtp.gtpcv2_pdu.suspendNotification.iMSI)) {
341 return gtp.gtpcv2_pdu.suspendNotification.iMSI.iMSI_Value;
342 }
343 } else if (ischosen(gtp.gtpcv2_pdu.resumeNotification)) {
344 if (ispresent(gtp.gtpcv2_pdu.resumeNotification.iMSI)) {
345 return gtp.gtpcv2_pdu.resumeNotification.iMSI.iMSI_Value;
346 }
347 } else if (ischosen(gtp.gtpcv2_pdu.cSPagingIndication)) {
348 if (ispresent(gtp.gtpcv2_pdu.cSPagingIndication.iMSI)) {
349 return gtp.gtpcv2_pdu.cSPagingIndication.iMSI.iMSI_Value;
350 }
351 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification)) {
352 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI)) {
353 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI.iMSI_Value;
354 }
355 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge)) {
356 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI)) {
357 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI.iMSI_Value;
358 }
359 } else if (ischosen(gtp.gtpcv2_pdu.traceSessionActivation)) {
360 if (ispresent(gtp.gtpcv2_pdu.traceSessionActivation.iMSI)) {
361 return gtp.gtpcv2_pdu.traceSessionActivation.iMSI.iMSI_Value;
362 }
363 }
364 return omit;
365}
366
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100367private function f_gtp2c_is_initial_msg(PDU_GTPCv2 msg) return boolean
368{
369 if (ischosen(msg.gtpcv2_pdu.echoRequest) or
370 ischosen(msg.gtpcv2_pdu.versionNotSupported) or
371 ischosen(msg.gtpcv2_pdu.createSessionRequest) or
372 ischosen(msg.gtpcv2_pdu.createBearerRequest) or
373 ischosen(msg.gtpcv2_pdu.bearerResourceCommand) or
374 ischosen(msg.gtpcv2_pdu.bearerResourceFailureIndication) or
375 ischosen(msg.gtpcv2_pdu.modifyBearerRequest) or
376 ischosen(msg.gtpcv2_pdu.deleteSessionRequest) or
377 ischosen(msg.gtpcv2_pdu.deleteBearerRequest) or
378 ischosen(msg.gtpcv2_pdu.downlinkDataNotification) or
379 ischosen(msg.gtpcv2_pdu.downlinkDataNotificationAcknowledgement) or
380 ischosen(msg.gtpcv2_pdu.downlinkDataNotificationFailureIndication) or
381 ischosen(msg.gtpcv2_pdu.deleteIndirectDataForwardingTunnelRequest) or
382 ischosen(msg.gtpcv2_pdu.modifyBearerCommand) or
383 ischosen(msg.gtpcv2_pdu.modifyBearerFailureIndication) or
384 ischosen(msg.gtpcv2_pdu.updateBearerRequest) or
385 ischosen(msg.gtpcv2_pdu.deleteBearerCommand) or
386 ischosen(msg.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest) or
387 ischosen(msg.gtpcv2_pdu.releaseAccessBearersRequest) or
388 ischosen(msg.gtpcv2_pdu.stopPagingIndication) or
389 ischosen(msg.gtpcv2_pdu.modifyAccessBearersRequest) or
390 ischosen(msg.gtpcv2_pdu.remoteUEReportNotification) or
391 ischosen(msg.gtpcv2_pdu.remoteUEReportAcknowledge) or
392 ischosen(msg.gtpcv2_pdu.forwardRelocationRequest) or
393 ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteNotification) or
394 ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteAcknowledge) or
395 ischosen(msg.gtpcv2_pdu.contextRequest) or
396 ischosen(msg.gtpcv2_pdu.contextAcknowledge) or
397 ischosen(msg.gtpcv2_pdu.identificationRequest) or
398 ischosen(msg.gtpcv2_pdu.forwardAccessContextNotification) or
399 ischosen(msg.gtpcv2_pdu.forwardAccessContextAcknowledge) or
400 ischosen(msg.gtpcv2_pdu.detachNotification) or
401 ischosen(msg.gtpcv2_pdu.detachAcknowledge) or
402 ischosen(msg.gtpcv2_pdu.changeNotificationRequest) or
403 ischosen(msg.gtpcv2_pdu.relocationCancelRequest) or
404 ischosen(msg.gtpcv2_pdu.configurationTransferTunnel) or
405 ischosen(msg.gtpcv2_pdu.rAN_InformationRelay) or
406 ischosen(msg.gtpcv2_pdu.suspendNotification) or
407 ischosen(msg.gtpcv2_pdu.suspendAcknowledge) or
408 ischosen(msg.gtpcv2_pdu.resumeNotification) or
409 ischosen(msg.gtpcv2_pdu.resumeAcknowledge) or
410 ischosen(msg.gtpcv2_pdu.cSPagingIndication) or
411 ischosen(msg.gtpcv2_pdu.createForwardingTunnelRequest) or
412 ischosen(msg.gtpcv2_pdu.deletePDN_ConnectionSetRequest) or
413 ischosen(msg.gtpcv2_pdu.traceSessionActivation) or
414 ischosen(msg.gtpcv2_pdu.traceSessionDeactivation) or
415 ischosen(msg.gtpcv2_pdu.updatePDN_ConnectionSetRequest) or
416 ischosen(msg.gtpcv2_pdu.pGW_RestartNotification) or
417 ischosen(msg.gtpcv2_pdu.pGW_RestartNotificationAcknowledge) or
418 ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringNotification) or
419 ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge) or
420 ischosen(msg.gtpcv2_pdu.alertMMENotification) or
421 ischosen(msg.gtpcv2_pdu.alertMMEAcknowledge) or
422 ischosen(msg.gtpcv2_pdu.uEActivityNotification) or
423 ischosen(msg.gtpcv2_pdu.uEActivityAcknowledge) or
424 ischosen(msg.gtpcv2_pdu.mBMSSessionStartRequest) or
425 ischosen(msg.gtpcv2_pdu.mBMSSessionUpdateRequest) or
426 ischosen(msg.gtpcv2_pdu.mBMSSessionStopRequest) or
427 ischosen(msg.gtpcv2_pdu.iSR_StatusIndication) or
428 ischosen(msg.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
429 return true;
430 }
431 return false;
432}
433
Harald Welte88b3ccb2020-03-12 21:36:32 +0100434private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
435 sinfo_stream := omit,
436 sinfo_ppid := ppid,
437 remSocks := omit,
438 assocId := omit
439};
440
441function tr_UECUPS_RecvFrom_R(template PDU_UECUPS msg)
442runs on GTPv2_Emulation_CT return template UECUPS_RecvFrom {
443 var template UECUPS_RecvFrom mrf := {
444 connId := g_uecups_conn_id,
445 remName := ?,
446 remPort := ?,
447 locName := ?,
448 locPort := ?,
449 msg := msg
450 }
451 return mrf;
452}
453
454
455private template PortEvent tr_SctpAssocChange := {
456 sctpEvent := {
457 sctpAssocChange := ?
458 }
459}
460private template PortEvent tr_SctpPeerAddrChange := {
461 sctpEvent := {
462 sctpPeerAddrChange := ?
463 }
464}
465
466private function f_uecups_xceive(template (value) PDU_UECUPS tx,
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200467 template PDU_UECUPS rx_t := ?, float time_out := 10.0)
Harald Welte88b3ccb2020-03-12 21:36:32 +0100468runs on GTPv2_Emulation_CT return PDU_UECUPS {
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200469 timer T := time_out;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100470 var UECUPS_RecvFrom mrf;
471
472 UECUPS.send(t_UECUPS_Send(g_uecups_conn_id, tx));
Vadim Yanitskiy5313af92022-01-20 18:53:15 +0600473 T.start;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100474 alt {
475 [] UECUPS.receive(tr_UECUPS_RecvFrom_R(rx_t)) -> value mrf { }
476 [] UECUPS.receive(tr_SctpAssocChange) { repeat; }
477 [] UECUPS.receive(tr_SctpPeerAddrChange) { repeat; }
478 [] T.timeout {
479 setverdict(fail, "Timeout waiting for ", rx_t);
480 mtc.stop;
481 }
482 }
483 return mrf.msg;
484}
485
486private function f_init(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
487 var Result res;
488
489 map(self:GTP2C, system:GTP2C);
490 res := GTPv2_CodecPort_CtrlFunct.f_IPL4_listen(GTP2C, cfg.gtpc_bind_ip,
491 cfg.gtpc_bind_port, {udp:={}});
492 g_gtp2c_id := res.connId;
493
494 g_restart_ctr := f_rnd_octstring(1);
495 g_c_seq_nr := f_rnd_int(65535);
496 g_gtp2_cfg := cfg;
497 g_peer := {
498 connId := g_gtp2c_id,
499 remName := g_gtp2_cfg.gtpc_remote_ip,
500 remPort := g_gtp2_cfg.gtpc_remote_port
501 }
502
Philipp Maierb11ee502023-08-31 16:48:19 +0200503 g_uecups_conn_id := res.connId;
504
Harald Welte88b3ccb2020-03-12 21:36:32 +0100505 if (g_gtp2_cfg.use_gtpu_daemon) {
506 map(self:UECUPS, system:UECUPS);
507 res := UECUPS_CodecPort_CtrlFunct.f_IPL4_connect(UECUPS, mp_uecups_host, mp_uecups_port, "", -1, -1, { sctp := valueof(ts_SCTP) });
508 if (not ispresent(res.connId)) {
509 setverdict(fail, "Could not connect UECUPS socket, check your configuration");
510 testcase.stop;
511 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100512
513 /* clear all tunnel state in the daemon at start */
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200514 f_uecups_xceive({reset_all_state := {}}, {reset_all_state_res:=?}, 30.0);
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100515 } else if (isvalue(cfg.gtpu_bind_ip) and isvalue(cfg.gtpu_bind_port)) {
516 map(self:GTPU, system:GTPU);
517 res := GTPv1U_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
518 cfg.gtpu_bind_port, {udp:={}});
519 g_gtp1u_id := res.connId;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100520 }
521
522 /* make sure we always pass incoming UECUPS indications whenever receiving fom the UECUPS port */
523 activate(as_uecups_ind());
524}
525
526private altstep as_uecups_ind() runs on GTPv2_Emulation_CT {
527var UECUPS_RecvFrom rx;
528var GTP2_ConnHdlr vc_conn;
529/* handle incoming program_term_ind; dispatch to whatever component started the process */
530[] UECUPS.receive(tr_UECUPS_RecvFrom_R({program_term_ind:=?})) -> value rx {
531 vc_conn := f_comp_by_pid(rx.msg.program_term_ind.pid);
532 CLIENT.send(rx.msg.program_term_ind) to vc_conn;
533 /* FIXME: remove from table */
534 repeat;
535 }
536}
537
Philipp Maierac791562023-09-01 17:10:24 +0200538private function SendToUdMsgTable(Gtp2cUnitdata g2c_ud) runs on GTPv2_Emulation_CT {
539 var GTP2_ConnHdlr vc_conn;
540
541 for (var integer i := 0; i < sizeof(UdMsgTable); i := i + 1) {
542 if (isbound(UdMsgTable[i].messageType)) {
543 if (UdMsgTable[i].messageType == g2c_ud.gtpc.messageType) {
544 vc_conn := UdMsgTable[i].vc_conn;
545 CLIENT.send(g2c_ud.gtpc) to vc_conn;
546 }
547 }
548 }
549
550 return;
551}
552
Harald Welte88b3ccb2020-03-12 21:36:32 +0100553function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
554 var Gtp2cUnitdata g2c_ud;
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100555 var Gtp1uUnitdata g1u_ud;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100556 var PDU_GTPCv2 g2c;
557 var GTP2_ConnHdlr vc_conn;
558 var hexstring imsi;
Philipp Maierac791562023-09-01 17:10:24 +0200559 var OCT1 messageType;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100560 var OCT4 teid;
561 var PDU_UECUPS rx_uecups;
562 var UECUPS_CreateTun gtc;
563 var UECUPS_DestroyTun gtd;
564 var UECUPS_StartProgram sprog;
565
566 f_init(cfg);
567
568 while (true) {
569 alt {
570 /* route inbound GTP2-C based on TEID, SEQ or IMSI */
571 [] GTP2C.receive(Gtp2cUnitdata:?) -> value g2c_ud {
572 var template hexstring imsi_t := f_gtp2c_extract_imsi(g2c_ud.gtpc);
Pau Espin Pedrol167bca42024-01-09 12:14:20 +0100573 /* if this is a response, route by SEQ: */
574 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))
575 and f_seq_known(g2c_ud.gtpc.sequenceNumber)) {
576 vc_conn := f_comp_by_seq(g2c_ud.gtpc.sequenceNumber);
577 CLIENT.send(g2c_ud.gtpc) to vc_conn;
578 }else if (isvalue(imsi_t) and f_imsi_known(valueof(imsi_t))) {
Harald Welte88b3ccb2020-03-12 21:36:32 +0100579 vc_conn := f_comp_by_imsi(valueof(imsi_t));
580 CLIENT.send(g2c_ud.gtpc) to vc_conn;
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100581 } else if ((ispresent(g2c_ud.gtpc.tEID) and g2c_ud.gtpc.tEID != '00000000'O)
582 and f_teid_known(g2c_ud.gtpc.tEID)) {
583 vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
584 CLIENT.send(g2c_ud.gtpc) to vc_conn;
585 } else if ((not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O)
586 and f_teid_known('00000000'O)) {
587 vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
588 CLIENT.send(g2c_ud.gtpc) to vc_conn;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100589 } else {
Pau Espin Pedrol1d2cc672024-01-05 22:06:16 +0100590 SendToUdMsgTable(g2c_ud);
Pau Espin Pedrol167bca42024-01-09 12:14:20 +0100591 if (not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O) {
592 TEID0.send(g2c_ud.gtpc);
593 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100594 }
595
596 /* remove sequence number if response was received */
597 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))) {
598 f_seq_tbl_del(g2c_ud.gtpc.sequenceNumber);
599 }
600
601 }
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100602 [] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
603 if (f_teid_known(g1u_ud.gtpu.teid)) {
604 vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
605 CLIENT.send(g1u_ud) to vc_conn;
606 } else if (g1u_ud.gtpu.teid == '00000000'O) {
607 TEID0.send(g1u_ud);
608 } else {
609 log("No client registered for TEID=", g1u_ud.gtpu.teid, "!");
610 }
611 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100612
613 [] TEID0.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100614 /* patch in the next sequence number on outbound Initial message */
615 if (f_gtp2c_is_initial_msg(g2c)) {
616 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
617 g_c_seq_nr := g_c_seq_nr + 1;
618 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100619 /* build Gtp2cUnitdata */
620 g2c_ud := { peer := g_peer, gtpc := g2c };
621 GTP2C.send(g2c_ud);
622 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
623 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
624 }
625 }
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100626 [] TEID0.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
627 GTPU.send(g1u_ud);
628 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100629
630 [] CLIENT.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
Pau Espin Pedrold7ae2c42023-12-13 19:11:13 +0100631 /* patch in the next sequence number on outbound Initial message */
632 if (f_gtp2c_is_initial_msg(g2c)) {
633 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
634 g_c_seq_nr := g_c_seq_nr + 1;
635 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100636 /* build Gtp2cUnitdata */
637 g2c_ud := { peer := g_peer, gtpc := g2c };
638 GTP2C.send(g2c_ud);
639 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
640 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
641 }
642 }
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100643 [] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
644 GTPU.send(g1u_ud);
645 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100646
647 [] CLIENT_PROC.getcall(GTP2EM_register_imsi:{?}) -> param(imsi) sender vc_conn {
648 f_imsi_tbl_add(imsi, vc_conn);
649 CLIENT_PROC.reply(GTP2EM_register_imsi:{imsi}) to vc_conn;
650 }
Philipp Maierac791562023-09-01 17:10:24 +0200651 [] CLIENT_PROC.getcall(GTP2EM_register_udmsg:{?}) -> param(messageType) sender vc_conn {
652 f_udmsg_tbl_add(messageType, vc_conn);
653 CLIENT_PROC.reply(GTP2EM_register_udmsg:{messageType}) to vc_conn;
654 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100655
656 [] CLIENT_PROC.getcall(GTP2EM_register_teid:{?}) -> param(teid) sender vc_conn {
657 f_tid_tbl_add(teid, vc_conn);
658 CLIENT_PROC.reply(GTP2EM_register_teid:{teid}) to vc_conn;
659 }
660 [] CLIENT_PROC.getcall(GTP2EM_allocate_teid:{}) -> sender vc_conn {
661 var OCT4 t := f_alloc_teid();
662 f_tid_tbl_add(t, vc_conn);
663 CLIENT_PROC.reply(GTP2EM_allocate_teid:{} value t) to vc_conn;
664 }
665 [] CLIENT_PROC.getcall(GTP2EM_create_tunnel:{?}) -> param(gtc) sender vc_conn {
666 rx_uecups := f_uecups_xceive({create_tun := gtc}, {create_tun_res:={result:=OK}});
667 CLIENT_PROC.reply(GTP2EM_create_tunnel:{gtc}) to vc_conn;
668 }
669 [] CLIENT_PROC.getcall(GTP2EM_destroy_tunnel:{?}) -> param(gtd) sender vc_conn {
670 rx_uecups := f_uecups_xceive({destroy_tun := gtd}, {destroy_tun_res:={result:=OK}});
671 CLIENT_PROC.reply(GTP2EM_destroy_tunnel:{gtd}) to vc_conn;
672 }
673 [] CLIENT_PROC.getcall(GTP2EM_start_program:{?}) -> param(sprog) sender vc_conn {
674 rx_uecups := f_uecups_xceive({start_program := sprog}, {start_program_res:=?});
675 /* if successful: store (pid, vc_conn) tuple so we can route program_term_ind */
676 if (rx_uecups.start_program_res.result == OK) {
677 f_pid_tbl_add(rx_uecups.start_program_res.pid, vc_conn);
678 }
679 CLIENT_PROC.reply(GTP2EM_start_program:{sprog} value rx_uecups.start_program_res) to vc_conn;
680 }
681
682 }
683 }
684}
685
686
687/***********************************************************************
688 * Interaction between Main and Client Components
689 ***********************************************************************/
690type port GTP2EM_PT message {
Pau Espin Pedrol65a7f762024-02-29 21:07:58 +0100691 inout PDU_GTPCv2, Gtp1uUnitdata, UECUPS_ProgramTermInd;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100692} with { extension "internal" };
693
694signature GTP2EM_register_imsi(hexstring imsi);
Philipp Maierac791562023-09-01 17:10:24 +0200695signature GTP2EM_register_udmsg(OCT1 messageType);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100696signature GTP2EM_register_teid(OCT4 teid);
697signature GTP2EM_allocate_teid() return OCT4;
698signature GTP2EM_create_tunnel(UECUPS_CreateTun gtc);
699signature GTP2EM_destroy_tunnel(UECUPS_DestroyTun gtd);
700signature GTP2EM_start_program(UECUPS_StartProgram sprog) return UECUPS_StartProgramRes;
701
702type port GTP2EM_PROC_PT procedure {
Philipp Maierac791562023-09-01 17:10:24 +0200703 inout GTP2EM_register_imsi, GTP2EM_register_udmsg, GTP2EM_register_teid, GTP2EM_allocate_teid,
Harald Welte88b3ccb2020-03-12 21:36:32 +0100704 GTP2EM_create_tunnel, GTP2EM_destroy_tunnel, GTP2EM_start_program;
705} with { extension "internal" };
706
707/***********************************************************************
708 * Client Component
709 ***********************************************************************/
710
711type component GTP2_ConnHdlr {
712 port GTP2EM_PT GTP2;
713 port GTP2EM_PROC_PT GTP2_PROC;
714};
715
716function f_gtp2_register_imsi(hexstring imsi) runs on GTP2_ConnHdlr {
Pau Espin Pedrol5c18a0c2023-10-19 13:41:41 +0200717 /* 15-digit IMSIs are len(imsi)=15, but decoded messages are
718 * octet-aligned, hence the hexstring in messages is len(imsi)=16, where
719 * the last hex char is a padding 'F'H */
720 imsi := f_pad_bcd_number(imsi);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100721 GTP2_PROC.call(GTP2EM_register_imsi:{imsi}) {
722 [] GTP2_PROC.getreply(GTP2EM_register_imsi:{imsi});
723 }
724}
725
Philipp Maierac791562023-09-01 17:10:24 +0200726function f_gtp2_register_udmsg(OCT1 messageType) runs on GTP2_ConnHdlr {
727 GTP2_PROC.call(GTP2EM_register_udmsg:{messageType}) {
728 [] GTP2_PROC.getreply(GTP2EM_register_udmsg:{messageType});
729 }
730}
731
Harald Welte88b3ccb2020-03-12 21:36:32 +0100732function f_gtp2_register_teid(OCT4 teid) runs on GTP2_ConnHdlr {
733 GTP2_PROC.call(GTP2EM_register_teid:{teid}) {
734 [] GTP2_PROC.getreply(GTP2EM_register_teid:{teid});
735 }
736}
737
738function f_gtp2_allocate_teid() runs on GTP2_ConnHdlr return OCT4 {
739 var OCT4 t;
740 GTP2_PROC.call(GTP2EM_allocate_teid:{}) {
741 [] GTP2_PROC.getreply(GTP2EM_allocate_teid:{}) -> value t {
742 return t;
743 }
744 }
745}
746
747function f_gtp2_create_tunnel(template (value) UECUPS_CreateTun gtc)
748runs on GTP2_ConnHdlr {
749 GTP2_PROC.call(GTP2EM_create_tunnel:{valueof(gtc)}) {
750 [] GTP2_PROC.getreply(GTP2EM_create_tunnel:{gtc});
751 }
752}
753
754function f_gtp2_destroy_tunnel(template (value) UECUPS_DestroyTun gtd)
755runs on GTP2_ConnHdlr {
756 GTP2_PROC.call(GTP2EM_destroy_tunnel:{valueof(gtd)}) {
757 [] GTP2_PROC.getreply(GTP2EM_destroy_tunnel:{gtd});
758 }
759}
760
761function f_gtp2_start_program(template (value) UECUPS_StartProgram sprog)
762runs on GTP2_ConnHdlr return UECUPS_StartProgramRes {
763 var UECUPS_StartProgramRes res;
764 GTP2_PROC.call(GTP2EM_start_program:{valueof(sprog)}) {
765 [] GTP2_PROC.getreply(GTP2EM_start_program:{sprog}) -> value res;
766 }
767 return res;
768}
769
770
771
772}