blob: f7c54a195b247ba3af98bdc158620d952ff70194 [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* GTP Emulation in TTCN-3
2 *
3 * (C) 2018 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
Harald Weltec69cf4e2018-02-17 20:57:02 +010012module GTP_Emulation {
13
14import from IPL4asp_Types all;
15import from General_Types all;
16import from Osmocom_Types all;
17import from GTPC_Types all;
18import from GTPU_Types all;
Pau Espin Pedrol4b090c92024-02-29 19:47:07 +010019import from GTPv1C_CodecPort all;
20import from GTPv1U_CodecPort all;
21import from GTPv1C_CodecPort_CtrlFunct all;
22import from GTPv1U_CodecPort_CtrlFunct all;
Harald Weltec69cf4e2018-02-17 20:57:02 +010023
24/***********************************************************************
25 * Main Emulation Component
26 ***********************************************************************/
27
28const integer GTP0_PORT := 3386;
29const integer GTP1C_PORT := 2123;
30const integer GTP1U_PORT := 2152;
31
32type record GtpEmulationCfg {
33 HostName gtpc_bind_ip,
34 PortNumber gtpc_bind_port,
Philipp Maier5c1b5f22023-07-12 14:03:52 +020035 HostName gtpu_bind_ip optional,
36 PortNumber gtpu_bind_port optional,
Harald Weltec69cf4e2018-02-17 20:57:02 +010037 boolean sgsn_role
38};
39
40type component GTP_Emulation_CT {
41 /* Communication with underlying GTP CodecPort */
42 port GTPC_PT GTPC;
43 port GTPU_PT GTPU;
44
45 /* Communication with Clients */
46 port GTPEM_PT CLIENT;
47 port GTPEM_PROC_PT CLIENT_PROC;
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +020048 port GTPEM_PT CLIENT_DEFAULT;
Harald Weltec69cf4e2018-02-17 20:57:02 +010049
50 /* Configuration by the user */
51 var GtpEmulationCfg g_gtp_cfg;
52
53 /* State */
54 var integer g_gtpc_id, g_gtpu_id;
55 var OCT1 g_restart_ctr;
56 var uint16_t g_c_seq_nr, g_u_seq_nr;
57 var TidTableRec TidTable[16];
58 var ImsiTableRec ImsiTable[16];
59};
60
61type record TidTableRec {
62 OCT4 teid,
63 GTP_ConnHdlr vc_conn
64};
65
66type record ImsiTableRec {
67 hexstring imsi,
68 GTP_ConnHdlr vc_conn
69};
70
71private function f_comp_by_teid(OCT4 teid) runs on GTP_Emulation_CT return GTP_ConnHdlr {
72 var integer i;
73 for (i := 0; i < sizeof(TidTable); i := i+1) {
74 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
75 return TidTable[i].vc_conn;
76 }
77 }
78 setverdict(fail, "No Component for TEID ", teid);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020079 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010080}
81
82private function f_comp_by_imsi(hexstring imsi) runs on GTP_Emulation_CT return GTP_ConnHdlr {
83 var integer i;
84 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
85 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
86 return ImsiTable[i].vc_conn;
87 }
88 }
89 setverdict(fail, "No Component for IMSI ", imsi);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020090 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010091}
92
93private function f_tid_tbl_add(OCT4 teid, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
94 var integer i;
95 for (i := 0; i < sizeof(TidTable); i := i+1) {
96 if (not isbound(TidTable[i].teid)) {
97 TidTable[i].teid := teid;
98 TidTable[i].vc_conn := vc_conn;
99 return;
100 }
101 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200102 testcase.stop("No Space in TidTable for ", teid);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100103}
104
105private function f_imsi_tbl_add(hexstring imsi, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
106 var integer i;
107 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
108 if (not isbound(ImsiTable[i].imsi)) {
109 ImsiTable[i].imsi := imsi;
110 ImsiTable[i].vc_conn := vc_conn;
111 return;
112 }
113 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200114 testcase.stop("No Space in IMSI Table for ", imsi);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100115}
116
117function f_gtpc_extract_imsi(PDU_GTPC gtp) return template (omit) hexstring {
118 if (ischosen(gtp.gtpc_pdu.createPDPContextRequest)) {
119 return gtp.gtpc_pdu.createPDPContextRequest.imsi.digits;
120 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN)) {
121 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN.imsi.digits;
122 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN)) {
123 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN.imsi.digits;
124 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW)) {
125 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW.imsi.digits;
126 } else if (ischosen(gtp.gtpc_pdu.pdu_NotificationRequest)) {
127 return gtp.gtpc_pdu.pdu_NotificationRequest.imsi.digits;
128 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest)) {
129 return gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest.imsi.digits;
130 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse)) {
131 return gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse.imsi.digits;
132 } else if (ischosen(gtp.gtpc_pdu.failureReportRequest)) {
133 return gtp.gtpc_pdu.failureReportRequest.imsi.digits;
134 } else if (ischosen(gtp.gtpc_pdu.noteMS_GPRSPresentRequest)) {
135 return gtp.gtpc_pdu.noteMS_GPRSPresentRequest.imsi.digits;
136 } else if (ischosen(gtp.gtpc_pdu.identificationResponse) ){
137 return gtp.gtpc_pdu.identificationResponse.imsi.digits;
138 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextRequest)) {
Pau Espin Pedrol12cf40a2024-01-02 17:45:12 +0100139 if (ispresent(gtp.gtpc_pdu.sgsn_ContextRequest.imsi.digits)) {
140 return gtp.gtpc_pdu.sgsn_ContextRequest.imsi.digits;
141 } else {
142 return omit;
143 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100144 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextResponse)) {
Pau Espin Pedrol22a65e62023-12-15 17:22:23 +0100145 if (ispresent(gtp.gtpc_pdu.sgsn_ContextResponse.imsi.digits)) {
146 return gtp.gtpc_pdu.sgsn_ContextResponse.imsi.digits;
147 } else {
148 return omit;
149 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100150 } else if (ischosen(gtp.gtpc_pdu.forwardRelocationRequest)) {
151 return gtp.gtpc_pdu.forwardRelocationRequest.imsi.digits;
152 } else if (ischosen(gtp.gtpc_pdu.relocationCancelRequest)) {
153 return gtp.gtpc_pdu.relocationCancelRequest.imsi.digits;
154 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryRequest)) {
155 return gtp.gtpc_pdu.uERegistrationQueryRequest.imsi.digits;
156 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryResponse)) {
157 return gtp.gtpc_pdu.uERegistrationQueryResponse.imsi.digits;
158 } else if (ischosen(gtp.gtpc_pdu.mBMSNotificationRequest)) {
159 return gtp.gtpc_pdu.mBMSNotificationRequest.imsi.digits;
160 } else if (ischosen(gtp.gtpc_pdu.createMBMSContextRequest)) {
161 return gtp.gtpc_pdu.createMBMSContextRequest.imsi.digits;
162 } else if (ischosen(gtp.gtpc_pdu.deleteMBMSContextRequest)) {
163 return gtp.gtpc_pdu.deleteMBMSContextRequest.imsi.digits;
164 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationRequest)) {
165 return gtp.gtpc_pdu.mS_InfoChangeNotificationRequest.imsi.digits;
166 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationResponse)) {
167 return gtp.gtpc_pdu.mS_InfoChangeNotificationResponse.imsi.digits;
168 } else {
169 return omit;
170 }
171}
172
173private function f_init(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
174 var Result res;
175
176 map(self:GTPC, system:GTPC);
Pau Espin Pedrol4b090c92024-02-29 19:47:07 +0100177 res := GTPv1C_CodecPort_CtrlFunct.f_IPL4_listen(GTPC, cfg.gtpc_bind_ip,
Harald Weltec69cf4e2018-02-17 20:57:02 +0100178 cfg.gtpc_bind_port, {udp:={}});
179 g_gtpc_id := res.connId;
180
Philipp Maier5c1b5f22023-07-12 14:03:52 +0200181 if (isvalue(cfg.gtpu_bind_ip) and isvalue(cfg.gtpu_bind_port)) {
182 map(self:GTPU, system:GTPU);
Pau Espin Pedrol4b090c92024-02-29 19:47:07 +0100183 res := GTPv1U_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
Philipp Maier5c1b5f22023-07-12 14:03:52 +0200184 cfg.gtpu_bind_port, {udp:={}});
185 g_gtpu_id := res.connId;
186 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100187
188 g_restart_ctr := f_rnd_octstring(1);
189 g_c_seq_nr := f_rnd_int(65535);
190 g_u_seq_nr := f_rnd_int(65535);
191 g_gtp_cfg := cfg;
192}
193
194function main(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
195 var Gtp1cUnitdata g1c_ud;
196 var Gtp1uUnitdata g1u_ud;
197 var GTP_ConnHdlr vc_conn;
198 var hexstring imsi;
199 var OCT4 teid;
200
201 f_init(cfg);
202
203 while (true) {
204 alt {
205 /* route inbound GTP-C based on IMSI or TEID */
206 [] GTPC.receive(Gtp1cUnitdata:?) -> value g1c_ud {
207 var template hexstring imsi_t := f_gtpc_extract_imsi(g1c_ud.gtpc);
208 if (isvalue(imsi_t)) {
209 vc_conn := f_comp_by_imsi(valueof(imsi_t));
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200210 CLIENT.send(g1c_ud) to vc_conn;
211 } else if(g1c_ud.gtpc.teid != int2oct(0, 4)) {
Harald Weltec69cf4e2018-02-17 20:57:02 +0100212 vc_conn := f_comp_by_teid(g1c_ud.gtpc.teid);
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200213 CLIENT.send(g1c_ud) to vc_conn;
214 } else {
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +0200215 /* Check if a default port is set: */
216 if (CLIENT_DEFAULT.checkstate("Connected")) {
217 CLIENT_DEFAULT.send(g1c_ud);
218 } else {
219 /* Send to all clients */
220 var integer i;
221 for (i := 0; i < sizeof(TidTable); i := i+1) {
222 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
223 CLIENT.send(g1c_ud) to TidTable[i].vc_conn;
224 }
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200225 }
226 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100227 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100228 }
229 [] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
230 vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
231 CLIENT.send(g1u_ud) to vc_conn;
232 }
233
234 /* transparently forward any GTP-C / GTP-U from clients to peer[s] */
235 [] CLIENT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
236 GTPC.send(g1c_ud);
237 }
238 [] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
239 GTPU.send(g1u_ud);
240 }
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +0200241 [] CLIENT_DEFAULT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
242 GTPC.send(g1c_ud);
243 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100244
245 [] CLIENT_PROC.getcall(GTPEM_register_imsi:{?}) -> param(imsi) sender vc_conn {
246 f_imsi_tbl_add(imsi, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200247 CLIENT_PROC.reply(GTPEM_register_imsi:{imsi}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100248 }
249
250 [] CLIENT_PROC.getcall(GTPEM_register_teid:{?}) -> param(teid) sender vc_conn {
251 f_tid_tbl_add(teid, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200252 CLIENT_PROC.reply(GTPEM_register_teid:{teid}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100253 }
254
255 }
256 }
257}
258
259
260/***********************************************************************
261 * Interaction between Main and Client Components
262 ***********************************************************************/
263type port GTPEM_PT message {
264 inout Gtp1cUnitdata, Gtp1uUnitdata;
265} with { extension "internal" };
266
267signature GTPEM_register_imsi(hexstring imsi);
268signature GTPEM_register_teid(OCT4 teid);
269
270type port GTPEM_PROC_PT procedure {
271 inout GTPEM_register_imsi, GTPEM_register_teid;
272} with { extension "internal" };
273
274/***********************************************************************
Philipp Maierfca4e4f2024-03-11 14:49:35 +0100275 * Client Component
Harald Weltec69cf4e2018-02-17 20:57:02 +0100276 ***********************************************************************/
277
278type component GTP_ConnHdlr {
279 port GTPEM_PT GTP;
280 port GTPEM_PROC_PT GTP_PROC;
281};
282
283function f_gtp_register_imsi(hexstring imsi) runs on GTP_ConnHdlr {
284 GTP_PROC.call(GTPEM_register_imsi:{imsi}) {
285 [] GTP_PROC.getreply(GTPEM_register_imsi:{imsi});
286 }
287}
288
289function f_gtp_register_teid(OCT4 teid) runs on GTP_ConnHdlr {
290 GTP_PROC.call(GTPEM_register_teid:{teid}) {
291 [] GTP_PROC.getreply(GTPEM_register_teid:{teid});
292 }
293}
294
295}