blob: 43c034b8f82c2877d95e4e47ff13d68134d788ec [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,
33 HostName gtpu_bind_ip,
34 PortNumber gtpu_bind_port,
35 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;
46
47 /* Configuration by the user */
48 var GtpEmulationCfg g_gtp_cfg;
49
50 /* State */
51 var integer g_gtpc_id, g_gtpu_id;
52 var OCT1 g_restart_ctr;
53 var uint16_t g_c_seq_nr, g_u_seq_nr;
54 var TidTableRec TidTable[16];
55 var ImsiTableRec ImsiTable[16];
56};
57
58type record TidTableRec {
59 OCT4 teid,
60 GTP_ConnHdlr vc_conn
61};
62
63type record ImsiTableRec {
64 hexstring imsi,
65 GTP_ConnHdlr vc_conn
66};
67
68private function f_comp_by_teid(OCT4 teid) runs on GTP_Emulation_CT return GTP_ConnHdlr {
69 var integer i;
70 for (i := 0; i < sizeof(TidTable); i := i+1) {
71 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
72 return TidTable[i].vc_conn;
73 }
74 }
75 setverdict(fail, "No Component for TEID ", teid);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020076 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010077}
78
79private function f_comp_by_imsi(hexstring imsi) runs on GTP_Emulation_CT return GTP_ConnHdlr {
80 var integer i;
81 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
82 if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
83 return ImsiTable[i].vc_conn;
84 }
85 }
86 setverdict(fail, "No Component for IMSI ", imsi);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020087 mtc.stop;
Harald Weltec69cf4e2018-02-17 20:57:02 +010088}
89
90private function f_tid_tbl_add(OCT4 teid, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
91 var integer i;
92 for (i := 0; i < sizeof(TidTable); i := i+1) {
93 if (not isbound(TidTable[i].teid)) {
94 TidTable[i].teid := teid;
95 TidTable[i].vc_conn := vc_conn;
96 return;
97 }
98 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +020099 testcase.stop("No Space in TidTable for ", teid);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100100}
101
102private function f_imsi_tbl_add(hexstring imsi, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
103 var integer i;
104 for (i := 0; i < sizeof(ImsiTable); i := i+1) {
105 if (not isbound(ImsiTable[i].imsi)) {
106 ImsiTable[i].imsi := imsi;
107 ImsiTable[i].vc_conn := vc_conn;
108 return;
109 }
110 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200111 testcase.stop("No Space in IMSI Table for ", imsi);
Harald Weltec69cf4e2018-02-17 20:57:02 +0100112}
113
114function f_gtpc_extract_imsi(PDU_GTPC gtp) return template (omit) hexstring {
115 if (ischosen(gtp.gtpc_pdu.createPDPContextRequest)) {
116 return gtp.gtpc_pdu.createPDPContextRequest.imsi.digits;
117 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN)) {
118 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN.imsi.digits;
119 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN)) {
120 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN.imsi.digits;
121 } else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW)) {
122 return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW.imsi.digits;
123 } else if (ischosen(gtp.gtpc_pdu.pdu_NotificationRequest)) {
124 return gtp.gtpc_pdu.pdu_NotificationRequest.imsi.digits;
125 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest)) {
126 return gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest.imsi.digits;
127 } else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse)) {
128 return gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse.imsi.digits;
129 } else if (ischosen(gtp.gtpc_pdu.failureReportRequest)) {
130 return gtp.gtpc_pdu.failureReportRequest.imsi.digits;
131 } else if (ischosen(gtp.gtpc_pdu.noteMS_GPRSPresentRequest)) {
132 return gtp.gtpc_pdu.noteMS_GPRSPresentRequest.imsi.digits;
133 } else if (ischosen(gtp.gtpc_pdu.identificationResponse) ){
134 return gtp.gtpc_pdu.identificationResponse.imsi.digits;
135 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextRequest)) {
136 return gtp.gtpc_pdu.sgsn_ContextRequest.imsi.digits;
137 } else if (ischosen(gtp.gtpc_pdu.sgsn_ContextResponse)) {
138 return gtp.gtpc_pdu.sgsn_ContextResponse.imsi.digits;
139 } else if (ischosen(gtp.gtpc_pdu.forwardRelocationRequest)) {
140 return gtp.gtpc_pdu.forwardRelocationRequest.imsi.digits;
141 } else if (ischosen(gtp.gtpc_pdu.relocationCancelRequest)) {
142 return gtp.gtpc_pdu.relocationCancelRequest.imsi.digits;
143 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryRequest)) {
144 return gtp.gtpc_pdu.uERegistrationQueryRequest.imsi.digits;
145 } else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryResponse)) {
146 return gtp.gtpc_pdu.uERegistrationQueryResponse.imsi.digits;
147 } else if (ischosen(gtp.gtpc_pdu.mBMSNotificationRequest)) {
148 return gtp.gtpc_pdu.mBMSNotificationRequest.imsi.digits;
149 } else if (ischosen(gtp.gtpc_pdu.createMBMSContextRequest)) {
150 return gtp.gtpc_pdu.createMBMSContextRequest.imsi.digits;
151 } else if (ischosen(gtp.gtpc_pdu.deleteMBMSContextRequest)) {
152 return gtp.gtpc_pdu.deleteMBMSContextRequest.imsi.digits;
153 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationRequest)) {
154 return gtp.gtpc_pdu.mS_InfoChangeNotificationRequest.imsi.digits;
155 } else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationResponse)) {
156 return gtp.gtpc_pdu.mS_InfoChangeNotificationResponse.imsi.digits;
157 } else {
158 return omit;
159 }
160}
161
162private function f_init(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
163 var Result res;
164
165 map(self:GTPC, system:GTPC);
166 res := GTP_CodecPort_CtrlFunct.f_IPL4_listen(GTPC, cfg.gtpc_bind_ip,
167 cfg.gtpc_bind_port, {udp:={}});
168 g_gtpc_id := res.connId;
169
170 map(self:GTPU, system:GTPU);
171 res := GTP_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
172 cfg.gtpu_bind_port, {udp:={}});
173 g_gtpu_id := res.connId;
174
175 g_restart_ctr := f_rnd_octstring(1);
176 g_c_seq_nr := f_rnd_int(65535);
177 g_u_seq_nr := f_rnd_int(65535);
178 g_gtp_cfg := cfg;
179}
180
181function main(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
182 var Gtp1cUnitdata g1c_ud;
183 var Gtp1uUnitdata g1u_ud;
184 var GTP_ConnHdlr vc_conn;
185 var hexstring imsi;
186 var OCT4 teid;
187
188 f_init(cfg);
189
190 while (true) {
191 alt {
192 /* route inbound GTP-C based on IMSI or TEID */
193 [] GTPC.receive(Gtp1cUnitdata:?) -> value g1c_ud {
194 var template hexstring imsi_t := f_gtpc_extract_imsi(g1c_ud.gtpc);
195 if (isvalue(imsi_t)) {
196 vc_conn := f_comp_by_imsi(valueof(imsi_t));
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200197 CLIENT.send(g1c_ud) to vc_conn;
198 } else if(g1c_ud.gtpc.teid != int2oct(0, 4)) {
Harald Weltec69cf4e2018-02-17 20:57:02 +0100199 vc_conn := f_comp_by_teid(g1c_ud.gtpc.teid);
Pau Espin Pedrol20e16c12018-07-10 18:38:17 +0200200 CLIENT.send(g1c_ud) to vc_conn;
201 } else {
202 /* Send to all clients */
203 var integer i;
204 for (i := 0; i < sizeof(TidTable); i := i+1) {
205 if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
206 CLIENT.send(g1c_ud) to TidTable[i].vc_conn;
207 }
208 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100209 }
Harald Weltec69cf4e2018-02-17 20:57:02 +0100210 }
211 [] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
212 vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
213 CLIENT.send(g1u_ud) to vc_conn;
214 }
215
216 /* transparently forward any GTP-C / GTP-U from clients to peer[s] */
217 [] CLIENT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
218 GTPC.send(g1c_ud);
219 }
220 [] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
221 GTPU.send(g1u_ud);
222 }
223
224
225 [] CLIENT_PROC.getcall(GTPEM_register_imsi:{?}) -> param(imsi) sender vc_conn {
226 f_imsi_tbl_add(imsi, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200227 CLIENT_PROC.reply(GTPEM_register_imsi:{imsi}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100228 }
229
230 [] CLIENT_PROC.getcall(GTPEM_register_teid:{?}) -> param(teid) sender vc_conn {
231 f_tid_tbl_add(teid, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200232 CLIENT_PROC.reply(GTPEM_register_teid:{teid}) to vc_conn;
Harald Weltec69cf4e2018-02-17 20:57:02 +0100233 }
234
235 }
236 }
237}
238
239
240/***********************************************************************
241 * Interaction between Main and Client Components
242 ***********************************************************************/
243type port GTPEM_PT message {
244 inout Gtp1cUnitdata, Gtp1uUnitdata;
245} with { extension "internal" };
246
247signature GTPEM_register_imsi(hexstring imsi);
248signature GTPEM_register_teid(OCT4 teid);
249
250type port GTPEM_PROC_PT procedure {
251 inout GTPEM_register_imsi, GTPEM_register_teid;
252} with { extension "internal" };
253
254/***********************************************************************
255 * Client Compoennt
256 ***********************************************************************/
257
258type component GTP_ConnHdlr {
259 port GTPEM_PT GTP;
260 port GTPEM_PROC_PT GTP_PROC;
261};
262
263function f_gtp_register_imsi(hexstring imsi) runs on GTP_ConnHdlr {
264 GTP_PROC.call(GTPEM_register_imsi:{imsi}) {
265 [] GTP_PROC.getreply(GTPEM_register_imsi:{imsi});
266 }
267}
268
269function f_gtp_register_teid(OCT4 teid) runs on GTP_ConnHdlr {
270 GTP_PROC.call(GTPEM_register_teid:{teid}) {
271 [] GTP_PROC.getreply(GTPEM_register_teid:{teid});
272 }
273}
274
275}