blob: ff6a60a551cb0f55cd37c209ee36329cb7206fd3 [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];
72 var PidTableRec PidTable[256];
73 var integer g_uecups_conn_id;
74};
75
76/* local TEID <-> ConnHdlr mapping */
77type record TidTableRec {
78 OCT4 teid,
79 GTP2_ConnHdlr vc_conn
80};
81
82/* local SeqNr <-> ConnHdlr mapping (until a response is received */
83type record SeqTableRec {
84 OCT3 seq,
85 GTP2_ConnHdlr vc_conn
86};
87
88/* IMSI <-> ConnHdlr mapping */
89type record ImsiTableRec {
90 hexstring imsi,
91 GTP2_ConnHdlr vc_conn
92};
93
94/* pid <-> ConnHdlr mapping (for UECUPS process termination indication) */
95type record PidTableRec {
96 /* process ID of the running process */
97 integer pid,
98 /* component that started it */
99 GTP2_ConnHdlr vc_conn
100};
101
102private function f_comp_by_teid(OCT4 teid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
103 var integer i;
104 for (i := 0; i < sizeof(TidTable); i := i+1) {
105 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
106 return TidTable[i].vc_conn;
107 }
108 }
109 setverdict(fail, "No Component for TEID ", teid);
110 mtc.stop;
111}
112
113private function f_seq_known(OCT3 seq) runs on GTPv2_Emulation_CT return boolean {
114 var integer i;
115 for (i := 0; i < sizeof(SeqTable); i := i+1) {
116 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
117 return true;
118 }
119 }
120 return false;
121}
122
123private function f_comp_by_seq(OCT3 seq) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
124 var integer i;
125 for (i := 0; i < sizeof(SeqTable); i := i+1) {
126 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
127 return SeqTable[i].vc_conn;
128 }
129 }
130 setverdict(fail, "No Component for SEQ ", seq);
131 mtc.stop;
132}
133
134private function f_comp_by_imsi(hexstring imsi) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
135 var integer i;
136 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
137 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
138 return ImsiTable[i].vc_conn;
139 }
140 }
141 setverdict(fail, "No Component for IMSI ", imsi);
142 mtc.stop;
143}
144
145private function f_comp_by_pid(integer pid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
146 var integer i;
147 for (i := 0; i < sizeof(PidTable); i := i+1) {
148 if (isbound(PidTable[i].pid) and PidTable[i].pid == pid) {
149 /* fixme: remove */
150 return PidTable[i].vc_conn;
151 }
152 }
153 setverdict(fail, "No Component for PID ", pid);
154 mtc.stop;
155}
156
157private function f_tid_tbl_add(OCT4 teid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
158 var integer i;
159 for (i := 0; i < sizeof(TidTable); i := i+1) {
160 if (not isbound(TidTable[i].teid)) {
161 TidTable[i].teid := teid;
162 TidTable[i].vc_conn := vc_conn;
163 return;
164 }
165 }
166 testcase.stop("No Space in TidTable for ", teid);
167}
168
169private function f_seq_tbl_add(OCT3 seq, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
170 var integer i;
171 for (i := 0; i < sizeof(SeqTable); i := i+1) {
172 if (not isbound(SeqTable[i].seq)) {
173 SeqTable[i].seq := seq;
174 SeqTable[i].vc_conn := vc_conn;
175 return;
176 }
177 }
178 testcase.stop("No Space in SeqTable for ", seq);
179}
180
181private function f_seq_tbl_del(OCT3 seq) runs on GTPv2_Emulation_CT {
182 var integer i;
183 for (i := 0; i < sizeof(SeqTable); i := i+1) {
184 if (isbound(SeqTable[i].seq) and SeqTable[i].seq == seq) {
185 SeqTable[i] := {
186 seq := -,
187 vc_conn := null
188 }
189 }
190 }
191}
192
193private function f_imsi_tbl_add(hexstring imsi, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
194 var integer i;
195 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
196 if (not isbound(ImsiTable[i].imsi)) {
197 ImsiTable[i].imsi := imsi;
198 ImsiTable[i].vc_conn := vc_conn;
199 return;
200 }
201 }
202 testcase.stop("No Space in IMSI Table for ", imsi);
203}
204
205private function f_pid_tbl_add(integer pid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
206 var integer i;
207 for (i := 0; i < sizeof(PidTable); i := i+1) {
208 if (not isbound(PidTable[i].pid)) {
209 PidTable[i].pid := pid;
210 PidTable[i].vc_conn := vc_conn;
211 return;
212 }
213 }
214 testcase.stop("No Space in PID Table for ", pid);
215}
216
217
218/* allocate an unused local teid */
219private function f_alloc_teid() runs on GTPv2_Emulation_CT return OCT4 {
220 var OCT4 teid;
221 var integer i, j;
222 for (i := 0; i < 100; i := i+1) {
223 teid := f_rnd_octstring(4);
224 for (j := 0; j < sizeof(TidTable); j := j+1) {
225 if (isbound(TidTable) and TidTable[i].teid == teid) {
226 continue;
227 }
228 }
229 /* we iterated over all entries and found no match: great! */
230 return teid;
231 }
232 testcase.stop("Cannot find unused TEID after ", i, " attempts");
233}
234
235/* obtain the IMSI from a GTPv2C PDU, if there is any IMSI contained. The way how the TITAN
236 * GTPv2 decoders are structured (explict IE members rather than a list/set of generic IE structures)
237 * doesn't make this easy, but requires lots of boilerplate code. Oh well.. */
238function f_gtp2c_extract_imsi(PDU_GTPCv2 gtp) return template (omit) hexstring {
239 if (ischosen(gtp.gtpcv2_pdu.createSessionRequest)) {
240 if (ispresent(gtp.gtpcv2_pdu.createSessionRequest.iMSI)) {
241 return gtp.gtpcv2_pdu.createSessionRequest.iMSI.iMSI_Value;
242 }
243 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotification)) {
244 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotification.iMSI)) {
245 return gtp.gtpcv2_pdu.downlinkDataNotification.iMSI.iMSI_Value;
246 }
247 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement)) {
248 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI)) {
249 return gtp.gtpcv2_pdu.downlinkDataNotificationAcknowledgement.iMSI.iMSI_Value;
250 }
251 } else if (ischosen(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication)) {
252 if (ispresent(gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI)) {
253 return gtp.gtpcv2_pdu.downlinkDataNotificationFailureIndication.iMSI.iMSI_Value;
254 }
255 } else if (ischosen(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest)) {
256 if (ispresent(gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI)) {
257 return gtp.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest.iMSI.iMSI_Value;
258 }
259 } else if (ischosen(gtp.gtpcv2_pdu.stopPagingIndication)) {
260 if (ispresent(gtp.gtpcv2_pdu.stopPagingIndication.iMSI)) {
261 return gtp.gtpcv2_pdu.stopPagingIndication.iMSI.iMSI_Value;
262 }
263 } else if (ischosen(gtp.gtpcv2_pdu.forwardRelocationRequest)) {
264 if (ispresent(gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI)) {
265 return gtp.gtpcv2_pdu.forwardRelocationRequest.iMSI.iMSI_Value;
266 }
267 } else if (ischosen(gtp.gtpcv2_pdu.contextRequest)) {
268 if (ispresent(gtp.gtpcv2_pdu.contextRequest.iMSI)) {
269 return gtp.gtpcv2_pdu.contextRequest.iMSI.iMSI_Value;
270 }
271 } else if (ischosen(gtp.gtpcv2_pdu.identificationResponse)) {
272 if (ispresent(gtp.gtpcv2_pdu.identificationResponse.iMSI)) {
273 return gtp.gtpcv2_pdu.identificationResponse.iMSI.iMSI_Value;
274 }
275 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationRequest)) {
276 if (ispresent(gtp.gtpcv2_pdu.changeNotificationRequest)) {
277 return gtp.gtpcv2_pdu.changeNotificationRequest.iMSI.iMSI_Value;
278 }
279 } else if (ischosen(gtp.gtpcv2_pdu.changeNotificationResponse)) {
280 if (ispresent(gtp.gtpcv2_pdu.changeNotificationResponse.iMSI)) {
281 return gtp.gtpcv2_pdu.changeNotificationResponse.iMSI.iMSI_Value;
282 }
283 } else if (ischosen(gtp.gtpcv2_pdu.relocationCancelRequest)) {
284 if (ispresent(gtp.gtpcv2_pdu.relocationCancelRequest.iMSI)) {
285 return gtp.gtpcv2_pdu.relocationCancelRequest.iMSI.iMSI_Value;
286 }
287 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
288 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI)) {
289 return gtp.gtpcv2_pdu.uE_RegistrationQueryRequest.iMSI.iMSI_Value;
290 }
291 } else if (ischosen(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse)) {
292 if (ispresent(gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI)) {
293 return gtp.gtpcv2_pdu.uE_RegistrationQueryResponse.iMSI.iMSI_Value;
294 }
295 } else if (ischosen(gtp.gtpcv2_pdu.suspendNotification)) {
296 if (ispresent(gtp.gtpcv2_pdu.suspendNotification.iMSI)) {
297 return gtp.gtpcv2_pdu.suspendNotification.iMSI.iMSI_Value;
298 }
299 } else if (ischosen(gtp.gtpcv2_pdu.resumeNotification)) {
300 if (ispresent(gtp.gtpcv2_pdu.resumeNotification.iMSI)) {
301 return gtp.gtpcv2_pdu.resumeNotification.iMSI.iMSI_Value;
302 }
303 } else if (ischosen(gtp.gtpcv2_pdu.cSPagingIndication)) {
304 if (ispresent(gtp.gtpcv2_pdu.cSPagingIndication.iMSI)) {
305 return gtp.gtpcv2_pdu.cSPagingIndication.iMSI.iMSI_Value;
306 }
307 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification)) {
308 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI)) {
309 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringNotification.iMSI.iMSI_Value;
310 }
311 } else if (ischosen(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge)) {
312 if (ispresent(gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI)) {
313 return gtp.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge.iMSI.iMSI_Value;
314 }
315 } else if (ischosen(gtp.gtpcv2_pdu.traceSessionActivation)) {
316 if (ispresent(gtp.gtpcv2_pdu.traceSessionActivation.iMSI)) {
317 return gtp.gtpcv2_pdu.traceSessionActivation.iMSI.iMSI_Value;
318 }
319 }
320 return omit;
321}
322
323private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
324 sinfo_stream := omit,
325 sinfo_ppid := ppid,
326 remSocks := omit,
327 assocId := omit
328};
329
330function tr_UECUPS_RecvFrom_R(template PDU_UECUPS msg)
331runs on GTPv2_Emulation_CT return template UECUPS_RecvFrom {
332 var template UECUPS_RecvFrom mrf := {
333 connId := g_uecups_conn_id,
334 remName := ?,
335 remPort := ?,
336 locName := ?,
337 locPort := ?,
338 msg := msg
339 }
340 return mrf;
341}
342
343
344private template PortEvent tr_SctpAssocChange := {
345 sctpEvent := {
346 sctpAssocChange := ?
347 }
348}
349private template PortEvent tr_SctpPeerAddrChange := {
350 sctpEvent := {
351 sctpPeerAddrChange := ?
352 }
353}
354
355private function f_uecups_xceive(template (value) PDU_UECUPS tx,
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200356 template PDU_UECUPS rx_t := ?, float time_out := 10.0)
Harald Welte88b3ccb2020-03-12 21:36:32 +0100357runs on GTPv2_Emulation_CT return PDU_UECUPS {
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200358 timer T := time_out;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100359 var UECUPS_RecvFrom mrf;
360
361 UECUPS.send(t_UECUPS_Send(g_uecups_conn_id, tx));
Vadim Yanitskiy5313af92022-01-20 18:53:15 +0600362 T.start;
Harald Welte88b3ccb2020-03-12 21:36:32 +0100363 alt {
364 [] UECUPS.receive(tr_UECUPS_RecvFrom_R(rx_t)) -> value mrf { }
365 [] UECUPS.receive(tr_SctpAssocChange) { repeat; }
366 [] UECUPS.receive(tr_SctpPeerAddrChange) { repeat; }
367 [] T.timeout {
368 setverdict(fail, "Timeout waiting for ", rx_t);
369 mtc.stop;
370 }
371 }
372 return mrf.msg;
373}
374
375private function f_init(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
376 var Result res;
377
378 map(self:GTP2C, system:GTP2C);
379 res := GTPv2_CodecPort_CtrlFunct.f_IPL4_listen(GTP2C, cfg.gtpc_bind_ip,
380 cfg.gtpc_bind_port, {udp:={}});
381 g_gtp2c_id := res.connId;
382
383 g_restart_ctr := f_rnd_octstring(1);
384 g_c_seq_nr := f_rnd_int(65535);
385 g_gtp2_cfg := cfg;
386 g_peer := {
387 connId := g_gtp2c_id,
388 remName := g_gtp2_cfg.gtpc_remote_ip,
389 remPort := g_gtp2_cfg.gtpc_remote_port
390 }
391
Philipp Maierb11ee502023-08-31 16:48:19 +0200392 g_uecups_conn_id := res.connId;
393
Harald Welte88b3ccb2020-03-12 21:36:32 +0100394 if (g_gtp2_cfg.use_gtpu_daemon) {
395 map(self:UECUPS, system:UECUPS);
396 res := UECUPS_CodecPort_CtrlFunct.f_IPL4_connect(UECUPS, mp_uecups_host, mp_uecups_port, "", -1, -1, { sctp := valueof(ts_SCTP) });
397 if (not ispresent(res.connId)) {
398 setverdict(fail, "Could not connect UECUPS socket, check your configuration");
399 testcase.stop;
400 }
Harald Welte88b3ccb2020-03-12 21:36:32 +0100401
402 /* clear all tunnel state in the daemon at start */
Pau Espin Pedrol12c4aa82022-04-11 19:55:55 +0200403 f_uecups_xceive({reset_all_state := {}}, {reset_all_state_res:=?}, 30.0);
Harald Welte88b3ccb2020-03-12 21:36:32 +0100404 }
405
406 /* make sure we always pass incoming UECUPS indications whenever receiving fom the UECUPS port */
407 activate(as_uecups_ind());
408}
409
410private altstep as_uecups_ind() runs on GTPv2_Emulation_CT {
411var UECUPS_RecvFrom rx;
412var GTP2_ConnHdlr vc_conn;
413/* handle incoming program_term_ind; dispatch to whatever component started the process */
414[] UECUPS.receive(tr_UECUPS_RecvFrom_R({program_term_ind:=?})) -> value rx {
415 vc_conn := f_comp_by_pid(rx.msg.program_term_ind.pid);
416 CLIENT.send(rx.msg.program_term_ind) to vc_conn;
417 /* FIXME: remove from table */
418 repeat;
419 }
420}
421
422function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
423 var Gtp2cUnitdata g2c_ud;
424 var PDU_GTPCv2 g2c;
425 var GTP2_ConnHdlr vc_conn;
426 var hexstring imsi;
427 var OCT4 teid;
428 var PDU_UECUPS rx_uecups;
429 var UECUPS_CreateTun gtc;
430 var UECUPS_DestroyTun gtd;
431 var UECUPS_StartProgram sprog;
432
433 f_init(cfg);
434
435 while (true) {
436 alt {
437 /* route inbound GTP2-C based on TEID, SEQ or IMSI */
438 [] GTP2C.receive(Gtp2cUnitdata:?) -> value g2c_ud {
439 var template hexstring imsi_t := f_gtp2c_extract_imsi(g2c_ud.gtpc);
440 if (not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == int2oct(0, 4)) {
441 /* if this is a response, route by SEQ */
442 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))
443 and f_seq_known(g2c_ud.gtpc.sequenceNumber)) {
444 vc_conn := f_comp_by_seq(g2c_ud.gtpc.sequenceNumber);
445 CLIENT.send(g2c_ud.gtpc) to vc_conn;
446 } else {
447 TEID0.send(g2c_ud.gtpc);
448 }
449 } else if (ispresent(g2c_ud.gtpc.tEID) and g2c_ud.gtpc.tEID != int2oct(0, 4)) {
450 vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
451 CLIENT.send(g2c_ud.gtpc) to vc_conn;
452 } else if (isvalue(imsi_t)) {
453 vc_conn := f_comp_by_imsi(valueof(imsi_t));
454 CLIENT.send(g2c_ud.gtpc) to vc_conn;
455 } else {
456 /* Send to all clients */
457 var integer i;
458 for (i := 0; i < sizeof(TidTable); i := i+1) {
459 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
460 CLIENT.send(g2c_ud.gtpc) to TidTable[i].vc_conn;
461 }
462 }
463 }
464
465 /* remove sequence number if response was received */
466 if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))) {
467 f_seq_tbl_del(g2c_ud.gtpc.sequenceNumber);
468 }
469
470 }
471
472 [] TEID0.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
473 /* patch in the next sequence number */
474 /* FIXME: do this only for outbound requests */
475 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
476 g_c_seq_nr := g_c_seq_nr + 1;
477 /* build Gtp2cUnitdata */
478 g2c_ud := { peer := g_peer, gtpc := g2c };
479 GTP2C.send(g2c_ud);
480 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
481 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
482 }
483 }
484
485 [] CLIENT.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
486 /* patch in the next sequence number */
487 /* FIXME: do this only for outbound requests */
488 g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
489 g_c_seq_nr := g_c_seq_nr + 1;
490 /* build Gtp2cUnitdata */
491 g2c_ud := { peer := g_peer, gtpc := g2c };
492 GTP2C.send(g2c_ud);
493 if (match(g2c, tr_PDU_GTP2C_msgtypes(gtp2_requests))) {
494 f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
495 }
496 }
497
498 [] CLIENT_PROC.getcall(GTP2EM_register_imsi:{?}) -> param(imsi) sender vc_conn {
499 f_imsi_tbl_add(imsi, vc_conn);
500 CLIENT_PROC.reply(GTP2EM_register_imsi:{imsi}) to vc_conn;
501 }
502
503 [] CLIENT_PROC.getcall(GTP2EM_register_teid:{?}) -> param(teid) sender vc_conn {
504 f_tid_tbl_add(teid, vc_conn);
505 CLIENT_PROC.reply(GTP2EM_register_teid:{teid}) to vc_conn;
506 }
507 [] CLIENT_PROC.getcall(GTP2EM_allocate_teid:{}) -> sender vc_conn {
508 var OCT4 t := f_alloc_teid();
509 f_tid_tbl_add(t, vc_conn);
510 CLIENT_PROC.reply(GTP2EM_allocate_teid:{} value t) to vc_conn;
511 }
512 [] CLIENT_PROC.getcall(GTP2EM_create_tunnel:{?}) -> param(gtc) sender vc_conn {
513 rx_uecups := f_uecups_xceive({create_tun := gtc}, {create_tun_res:={result:=OK}});
514 CLIENT_PROC.reply(GTP2EM_create_tunnel:{gtc}) to vc_conn;
515 }
516 [] CLIENT_PROC.getcall(GTP2EM_destroy_tunnel:{?}) -> param(gtd) sender vc_conn {
517 rx_uecups := f_uecups_xceive({destroy_tun := gtd}, {destroy_tun_res:={result:=OK}});
518 CLIENT_PROC.reply(GTP2EM_destroy_tunnel:{gtd}) to vc_conn;
519 }
520 [] CLIENT_PROC.getcall(GTP2EM_start_program:{?}) -> param(sprog) sender vc_conn {
521 rx_uecups := f_uecups_xceive({start_program := sprog}, {start_program_res:=?});
522 /* if successful: store (pid, vc_conn) tuple so we can route program_term_ind */
523 if (rx_uecups.start_program_res.result == OK) {
524 f_pid_tbl_add(rx_uecups.start_program_res.pid, vc_conn);
525 }
526 CLIENT_PROC.reply(GTP2EM_start_program:{sprog} value rx_uecups.start_program_res) to vc_conn;
527 }
528
529 }
530 }
531}
532
533
534/***********************************************************************
535 * Interaction between Main and Client Components
536 ***********************************************************************/
537type port GTP2EM_PT message {
538 inout PDU_GTPCv2, UECUPS_ProgramTermInd;
539} with { extension "internal" };
540
541signature GTP2EM_register_imsi(hexstring imsi);
542signature GTP2EM_register_teid(OCT4 teid);
543signature GTP2EM_allocate_teid() return OCT4;
544signature GTP2EM_create_tunnel(UECUPS_CreateTun gtc);
545signature GTP2EM_destroy_tunnel(UECUPS_DestroyTun gtd);
546signature GTP2EM_start_program(UECUPS_StartProgram sprog) return UECUPS_StartProgramRes;
547
548type port GTP2EM_PROC_PT procedure {
549 inout GTP2EM_register_imsi, GTP2EM_register_teid, GTP2EM_allocate_teid,
550 GTP2EM_create_tunnel, GTP2EM_destroy_tunnel, GTP2EM_start_program;
551} with { extension "internal" };
552
553/***********************************************************************
554 * Client Component
555 ***********************************************************************/
556
557type component GTP2_ConnHdlr {
558 port GTP2EM_PT GTP2;
559 port GTP2EM_PROC_PT GTP2_PROC;
560};
561
562function f_gtp2_register_imsi(hexstring imsi) runs on GTP2_ConnHdlr {
563 GTP2_PROC.call(GTP2EM_register_imsi:{imsi}) {
564 [] GTP2_PROC.getreply(GTP2EM_register_imsi:{imsi});
565 }
566}
567
568function f_gtp2_register_teid(OCT4 teid) runs on GTP2_ConnHdlr {
569 GTP2_PROC.call(GTP2EM_register_teid:{teid}) {
570 [] GTP2_PROC.getreply(GTP2EM_register_teid:{teid});
571 }
572}
573
574function f_gtp2_allocate_teid() runs on GTP2_ConnHdlr return OCT4 {
575 var OCT4 t;
576 GTP2_PROC.call(GTP2EM_allocate_teid:{}) {
577 [] GTP2_PROC.getreply(GTP2EM_allocate_teid:{}) -> value t {
578 return t;
579 }
580 }
581}
582
583function f_gtp2_create_tunnel(template (value) UECUPS_CreateTun gtc)
584runs on GTP2_ConnHdlr {
585 GTP2_PROC.call(GTP2EM_create_tunnel:{valueof(gtc)}) {
586 [] GTP2_PROC.getreply(GTP2EM_create_tunnel:{gtc});
587 }
588}
589
590function f_gtp2_destroy_tunnel(template (value) UECUPS_DestroyTun gtd)
591runs on GTP2_ConnHdlr {
592 GTP2_PROC.call(GTP2EM_destroy_tunnel:{valueof(gtd)}) {
593 [] GTP2_PROC.getreply(GTP2EM_destroy_tunnel:{gtd});
594 }
595}
596
597function f_gtp2_start_program(template (value) UECUPS_StartProgram sprog)
598runs on GTP2_ConnHdlr return UECUPS_StartProgramRes {
599 var UECUPS_StartProgramRes res;
600 GTP2_PROC.call(GTP2EM_start_program:{valueof(sprog)}) {
601 [] GTP2_PROC.getreply(GTP2EM_start_program:{sprog}) -> value res;
602 }
603 return res;
604}
605
606
607
608}