blob: 4ea293c6cbb8b5872a21aa6c00e2c123daaca0ba [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;
19import from GTP_CodecPort all;
20import from GTP_CodecPort_CtrlFunct all;
21
22/***********************************************************************
23 * Main Emulation Component
24 ***********************************************************************/
25
26const integer GTP0_PORT := 3386;
27const integer GTP1C_PORT := 2123;
28const integer GTP1U_PORT := 2152;
29
30type record GtpEmulationCfg {
31 HostName gtpc_bind_ip,
32 PortNumber gtpc_bind_port,
Philipp Maier5c1b5f22023-07-12 14:03:52 +020033 HostName gtpu_bind_ip optional,
34 PortNumber gtpu_bind_port optional,
Harald Weltec69cf4e2018-02-17 20:57:02 +010035 boolean sgsn_role
36};
37
38type component GTP_Emulation_CT {
39 /* Communication with underlying GTP CodecPort */
40 port GTPC_PT GTPC;
41 port GTPU_PT GTPU;
42
43 /* Communication with Clients */
44 port GTPEM_PT CLIENT;
45 port GTPEM_PROC_PT CLIENT_PROC;
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +020046 port GTPEM_PT CLIENT_DEFAULT;
Harald Weltec69cf4e2018-02-17 20:57:02 +010047
48 /* Configuration by the user */
49 var GtpEmulationCfg g_gtp_cfg;
50
51 /* State */
52 var integer g_gtpc_id, g_gtpu_id;
53 var OCT1 g_restart_ctr;
54 var uint16_t g_c_seq_nr, g_u_seq_nr;
55 var TidTableRec TidTable[16];
56 var ImsiTableRec ImsiTable[16];
57};
58
59type record TidTableRec {
60 OCT4 teid,
61 GTP_ConnHdlr vc_conn
62};
63
64type record ImsiTableRec {
65 hexstring imsi,
66 GTP_ConnHdlr vc_conn
67};
68
69private function f_comp_by_teid(OCT4 teid) runs on GTP_Emulation_CT return GTP_ConnHdlr {
70 var integer i;
71 for (i := 0; i < sizeof(TidTable); i := i+1) {
72 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
73 return TidTable[i].vc_conn;
74 }
75 }
76 setverdict(fail, "No Component for TEID ", teid);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020077 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010078}
79
80private function f_comp_by_imsi(hexstring imsi) runs on GTP_Emulation_CT return GTP_ConnHdlr {
81 var integer i;
82 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
83 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
84 return ImsiTable[i].vc_conn;
85 }
86 }
87 setverdict(fail, "No Component for IMSI ", imsi);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020088 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010089}
90
91private function f_tid_tbl_add(OCT4 teid, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
92 var integer i;
93 for (i := 0; i < sizeof(TidTable); i := i+1) {
94 if (not isbound(TidTable[i].teid)) {
95 TidTable[i].teid := teid;
96 TidTable[i].vc_conn := vc_conn;
97 return;
98 }
99 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200100 testcase.stop("No Space in TidTable for ", teid);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100101}
102
103private function f_imsi_tbl_add(hexstring imsi, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
104 var integer i;
105 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
106 if (not isbound(ImsiTable[i].imsi)) {
107 ImsiTable[i].imsi := imsi;
108 ImsiTable[i].vc_conn := vc_conn;
109 return;
110 }
111 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200112 testcase.stop("No Space in IMSI Table for ", imsi);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100113}
114
115function f_gtpc_extract_imsi(PDU_GTPC gtp) return template (omit) hexstring {
116 if (ischosen(gtp.gtpc_pdu.createPDPContextRequest)) {
117 return gtp.gtpc_pdu.createPDPContextRequest.imsi.digits;
118 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN)) {
119 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN.imsi.digits;
120 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN)) {
121 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN.imsi.digits;
122 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW)) {
123 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW.imsi.digits;
124 } else if (ischosen(gtp.gtpc_pdu.pdu_NotificationRequest)) {
125 return gtp.gtpc_pdu.pdu_NotificationRequest.imsi.digits;
126 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest)) {
127 return gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest.imsi.digits;
128 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse)) {
129 return gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse.imsi.digits;
130 } else if (ischosen(gtp.gtpc_pdu.failureReportRequest)) {
131 return gtp.gtpc_pdu.failureReportRequest.imsi.digits;
132 } else if (ischosen(gtp.gtpc_pdu.noteMS_GPRSPresentRequest)) {
133 return gtp.gtpc_pdu.noteMS_GPRSPresentRequest.imsi.digits;
134 } else if (ischosen(gtp.gtpc_pdu.identificationResponse) ){
135 return gtp.gtpc_pdu.identificationResponse.imsi.digits;
136 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextRequest)) {
137 return gtp.gtpc_pdu.sgsn_ContextRequest.imsi.digits;
138 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextResponse)) {
139 return gtp.gtpc_pdu.sgsn_ContextResponse.imsi.digits;
140 } else if (ischosen(gtp.gtpc_pdu.forwardRelocationRequest)) {
141 return gtp.gtpc_pdu.forwardRelocationRequest.imsi.digits;
142 } else if (ischosen(gtp.gtpc_pdu.relocationCancelRequest)) {
143 return gtp.gtpc_pdu.relocationCancelRequest.imsi.digits;
144 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryRequest)) {
145 return gtp.gtpc_pdu.uERegistrationQueryRequest.imsi.digits;
146 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryResponse)) {
147 return gtp.gtpc_pdu.uERegistrationQueryResponse.imsi.digits;
148 } else if (ischosen(gtp.gtpc_pdu.mBMSNotificationRequest)) {
149 return gtp.gtpc_pdu.mBMSNotificationRequest.imsi.digits;
150 } else if (ischosen(gtp.gtpc_pdu.createMBMSContextRequest)) {
151 return gtp.gtpc_pdu.createMBMSContextRequest.imsi.digits;
152 } else if (ischosen(gtp.gtpc_pdu.deleteMBMSContextRequest)) {
153 return gtp.gtpc_pdu.deleteMBMSContextRequest.imsi.digits;
154 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationRequest)) {
155 return gtp.gtpc_pdu.mS_InfoChangeNotificationRequest.imsi.digits;
156 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationResponse)) {
157 return gtp.gtpc_pdu.mS_InfoChangeNotificationResponse.imsi.digits;
158 } else {
159 return omit;
160 }
161}
162
163private function f_init(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
164 var Result res;
165
166 map(self:GTPC, system:GTPC);
167 res := GTP_CodecPort_CtrlFunct.f_IPL4_listen(GTPC, cfg.gtpc_bind_ip,
168 cfg.gtpc_bind_port, {udp:={}});
169 g_gtpc_id := res.connId;
170
Philipp Maier5c1b5f22023-07-12 14:03:52 +0200171 if (isvalue(cfg.gtpu_bind_ip) and isvalue(cfg.gtpu_bind_port)) {
172 map(self:GTPU, system:GTPU);
173 res := GTP_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
174 cfg.gtpu_bind_port, {udp:={}});
175 g_gtpu_id := res.connId;
176 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100177
178 g_restart_ctr := f_rnd_octstring(1);
179 g_c_seq_nr := f_rnd_int(65535);
180 g_u_seq_nr := f_rnd_int(65535);
181 g_gtp_cfg := cfg;
182}
183
184function main(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
185 var Gtp1cUnitdata g1c_ud;
186 var Gtp1uUnitdata g1u_ud;
187 var GTP_ConnHdlr vc_conn;
188 var hexstring imsi;
189 var OCT4 teid;
190
191 f_init(cfg);
192
193 while (true) {
194 alt {
195 /* route inbound GTP-C based on IMSI or TEID */
196 [] GTPC.receive(Gtp1cUnitdata:?) -> value g1c_ud {
197 var template hexstring imsi_t := f_gtpc_extract_imsi(g1c_ud.gtpc);
198 if (isvalue(imsi_t)) {
199 vc_conn := f_comp_by_imsi(valueof(imsi_t));
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200200 CLIENT.send(g1c_ud) to vc_conn;
201 } else if(g1c_ud.gtpc.teid != int2oct(0, 4)) {
Harald Weltec69cf4e2018-02-17 20:57:02 +0100202 vc_conn := f_comp_by_teid(g1c_ud.gtpc.teid);
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200203 CLIENT.send(g1c_ud) to vc_conn;
204 } else {
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +0200205 /* Check if a default port is set: */
206 if (CLIENT_DEFAULT.checkstate("Connected")) {
207 CLIENT_DEFAULT.send(g1c_ud);
208 } else {
209 /* Send to all clients */
210 var integer i;
211 for (i := 0; i < sizeof(TidTable); i := i+1) {
212 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
213 CLIENT.send(g1c_ud) to TidTable[i].vc_conn;
214 }
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200215 }
216 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100217 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100218 }
219 [] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
220 vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
221 CLIENT.send(g1u_ud) to vc_conn;
222 }
223
224 /* transparently forward any GTP-C / GTP-U from clients to peer[s] */
225 [] CLIENT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
226 GTPC.send(g1c_ud);
227 }
228 [] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
229 GTPU.send(g1u_ud);
230 }
Pau Espin Pedrol8c74cbb2021-05-04 15:26:56 +0200231 [] CLIENT_DEFAULT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
232 GTPC.send(g1c_ud);
233 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100234
235 [] CLIENT_PROC.getcall(GTPEM_register_imsi:{?}) -> param(imsi) sender vc_conn {
236 f_imsi_tbl_add(imsi, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200237 CLIENT_PROC.reply(GTPEM_register_imsi:{imsi}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100238 }
239
240 [] CLIENT_PROC.getcall(GTPEM_register_teid:{?}) -> param(teid) sender vc_conn {
241 f_tid_tbl_add(teid, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200242 CLIENT_PROC.reply(GTPEM_register_teid:{teid}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100243 }
244
245 }
246 }
247}
248
249
250/***********************************************************************
251 * Interaction between Main and Client Components
252 ***********************************************************************/
253type port GTPEM_PT message {
254 inout Gtp1cUnitdata, Gtp1uUnitdata;
255} with { extension "internal" };
256
257signature GTPEM_register_imsi(hexstring imsi);
258signature GTPEM_register_teid(OCT4 teid);
259
260type port GTPEM_PROC_PT procedure {
261 inout GTPEM_register_imsi, GTPEM_register_teid;
262} with { extension "internal" };
263
264/***********************************************************************
265 * Client Compoennt
266 ***********************************************************************/
267
268type component GTP_ConnHdlr {
269 port GTPEM_PT GTP;
270 port GTPEM_PROC_PT GTP_PROC;
271};
272
273function f_gtp_register_imsi(hexstring imsi) runs on GTP_ConnHdlr {
274 GTP_PROC.call(GTPEM_register_imsi:{imsi}) {
275 [] GTP_PROC.getreply(GTPEM_register_imsi:{imsi});
276 }
277}
278
279function f_gtp_register_teid(OCT4 teid) runs on GTP_ConnHdlr {
280 GTP_PROC.call(GTPEM_register_teid:{teid}) {
281 [] GTP_PROC.getreply(GTPEM_register_teid:{teid});
282 }
283}
284
285}