blob: 86859baa8bdfe0f4f75aae076a66938c9de8dfad [file] [log] [blame]
Harald Welte0e5aad22018-01-21 14:00:41 +01001module GSUP_Emulation {
2
3/* GSUP Emulation, runs on top of IPA_Emulation. It multiplexes/demultiplexes
4 * the individual calls, so there can be separate TTCN-3 components handling
5 * each of the calls
6 *
7 * The GSUP_Emulation.main() function processes GSUP primitives from the IPA/GSUP
8 * socket via the IPA_Emulation, and dispatches them to the per-connection components.
9 *
10 * Outbound GSUP connections are initiated by sending a FIXME primitive
11 * to the component running the GSUP_Emulation.main() function.
12 *
13 * For each new inbound connections, the GsupOps.create_cb() is called. It can create
14 * or resolve a TTCN-3 component, and returns a component reference to which that inbound
15 * connection is routed/dispatched.
16 *
17 * If a pre-existing component wants to register to handle a future inbound call, it can
18 * do so by registering an "expect" with the expected destination phone number. This is e.g. useful
19 * if you are simulating BSC + HUL, and first trigger a connection from BSC side in a
20 * component which then subsequently should also handle the GSUP emulation.
21 *
22 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
23 * All rights reserved.
24 *
25 * Released under the terms of GNU General Public License, Version 2 or
26 * (at your option) any later version.
27 */
28
29
30import from Osmocom_Types all;
31import from GSUP_Types all;
32import from IPA_Emulation all;
33
34/* General "base class" component definition, of which specific implementations
35 * derive themselves by means of the "extends" feature */
36type component GSUP_ConnHdlr {
37 /* ports towards GSUP Emulator core / call dispatchar */
38 port GSUP_Conn_PT GSUP;
39 port GSUPEM_PROC_PT GSUP_PROC;
40}
41
42/* port between individual per-connection components and this dispatcher */
43type port GSUP_Conn_PT message {
44 inout GSUP_PDU;
45} with { extension "internal" };
46
47
48/* represents a single GSUP call */
49type record ConnectionData {
50 /* reference to the instance of the per-connection component */
51 GSUP_ConnHdlr comp_ref,
52 charstring imsi
53}
54
55type component GSUP_Emulation_CT {
56 /* UNIX DOMAIN socket on the bottom side, using primitives */
57 port IPA_GSUP_PT GSUP;
58 /* GSUP port to the per-connection clients */
59 port GSUP_Conn_PT GSUP_CLIENT;
60
61 /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
62 var ConnectionData GsupImsiTable[16];
63
64 /* pending expected incoming connections */
65 var ExpectData GsupExpectTable[8];
66 /* procedure based port to register for incoming connections */
67 port GSUPEM_PROC_PT GSUP_PROC;
68};
69
70private function f_imsi_known(charstring imsi)
71runs on GSUP_Emulation_CT return boolean {
72 var integer i;
73 for (i := 0; i < sizeof(GsupImsiTable); i := i+1) {
74 if (GsupImsiTable[i].imsi == imsi) {
75 return true;
76 }
77 }
78 return false;
79}
80
81private function f_comp_known(GSUP_ConnHdlr client)
82runs on GSUP_Emulation_CT return boolean {
83 var integer i;
84 for (i := 0; i < sizeof(GsupImsiTable); i := i+1) {
85 if (GsupImsiTable[i].comp_ref == client) {
86 return true;
87 }
88 }
89 return false;
90}
91
92/* resolve component reference by connection ID */
93private function f_comp_by_imsi(charstring imsi)
94runs on GSUP_Emulation_CT return GSUP_ConnHdlr {
95 var integer i;
96 for (i := 0; i < sizeof(GsupImsiTable); i := i+1) {
97 if (GsupImsiTable[i].imsi == imsi) {
98 return GsupImsiTable[i].comp_ref;
99 }
100 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200101 setverdict(fail, "GSUP IMSI table not found by IMSI ", imsi);
102 mtc.stop;
Harald Welte0e5aad22018-01-21 14:00:41 +0100103}
104
105/* resolve connection ID by component reference */
106private function f_imsi_by_comp(GSUP_ConnHdlr client)
107runs on GSUP_Emulation_CT return charstring {
108 for (var integer i := 0; i < sizeof(GsupImsiTable); i := i+1) {
109 if (GsupImsiTable[i].comp_ref == client) {
110 return GsupImsiTable[i].imsi;
111 }
112 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200113 setverdict(fail, "GSUP IMSI table not found by component ", client);
114 mtc.stop;
Harald Welte0e5aad22018-01-21 14:00:41 +0100115}
116
117private function f_imsi_table_init()
118runs on GSUP_Emulation_CT {
119 for (var integer i := 0; i < sizeof(GsupImsiTable); i := i+1) {
120 GsupImsiTable[i].comp_ref := null;
121 GsupImsiTable[i].imsi := "";
122 }
123}
124
Harald Welted95da562018-01-27 21:46:53 +0100125private function f_expect_table_init()
126runs on GSUP_Emulation_CT {
127 for (var integer i := 0; i < sizeof(GsupExpectTable); i := i+1) {
128 GsupExpectTable[i].vc_conn := null;
129 GsupExpectTable[i].imsi := omit;
130 }
131}
132
Harald Welte0e5aad22018-01-21 14:00:41 +0100133private function f_imsi_table_add(GSUP_ConnHdlr comp_ref, charstring imsi)
134runs on GSUP_Emulation_CT {
135 for (var integer i := 0; i < sizeof(GsupImsiTable); i := i+1) {
136 if (GsupImsiTable[i].imsi == "") {
137 GsupImsiTable[i].comp_ref := comp_ref;
138 GsupImsiTable[i].imsi := imsi;
139 log("Added IMSI table entry ", i, comp_ref, imsi);
140 return;
141 }
142 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200143 testcase.stop("GSUP IMSI table full!");
Harald Welte0e5aad22018-01-21 14:00:41 +0100144}
145
146private function f_imsi_table_del(charstring imsi)
147runs on GSUP_Emulation_CT {
148 for (var integer i := 0; i < sizeof(GsupImsiTable); i := i+1) {
149 if (GsupImsiTable[i].imsi == imsi) {
150 log("Deleted GSUP IMSI table entry ", i,
151 GsupImsiTable[i].comp_ref, imsi);
152 GsupImsiTable[i].imsi := "";
153 GsupImsiTable[i].comp_ref := null;
154 return
155 }
156 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200157 setverdict(fail, "GSUP IMSI table attempt to delete non-existant ", imsi);
158 mtc.stop;
Harald Welte0e5aad22018-01-21 14:00:41 +0100159}
160
161
162/* call-back type, to be provided by specific implementation; called when new SCCP connection
163 * arrives */
164type function GsupCreateCallback(GSUP_PDU gsup, charstring id)
165runs on GSUP_Emulation_CT return GSUP_ConnHdlr;
166
167type record GsupOps {
168 GsupCreateCallback create_cb
169}
170
171function main(GsupOps ops, charstring id) runs on GSUP_Emulation_CT {
172
173 f_imsi_table_init();
Harald Welted95da562018-01-27 21:46:53 +0100174 f_expect_table_init();
Harald Welte0e5aad22018-01-21 14:00:41 +0100175
176 while (true) {
177 var GSUP_ConnHdlr vc_conn;
178 var GSUP_ConnHdlr vc_hdlr;
179 var GSUP_PDU gsup;
180 var charstring imsi;
181
182 alt {
183
184 [] GSUP.receive(ASP_IPA_Event:{up_down:=ASP_IPA_EVENT_ID_ACK}) { repeat; }
185 [] GSUP.receive(ASP_IPA_Event:{up_down:=ASP_IPA_EVENT_UP}) { repeat; }
186 [] GSUP.receive(ASP_IPA_Event:{up_down:=ASP_IPA_EVENT_DOWN}) {
187 setverdict(fail, "GSUP Connection Lost");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200188 mtc.stop;
Harald Welte0e5aad22018-01-21 14:00:41 +0100189 }
190
191 /* GSUP -> Client: call related messages */
192 [] GSUP.receive(GSUP_PDU:?) -> value gsup {
193 imsi := hex2str(gsup.ies[0].val.imsi);
194
195 if (f_imsi_known(imsi)) {
196 vc_conn := f_comp_by_imsi(imsi);
197 GSUP_CLIENT.send(gsup) to vc_conn;
198 } else {
199 /* TODO: Only accept this for SETUP.req? */
200 vc_conn := ops.create_cb.apply(gsup, id)
201 /* store mapping between client components and SCCP connectionId */
202 f_imsi_table_add(vc_conn, imsi);
203 /* handle user payload */
204 GSUP_CLIENT.send(gsup) to vc_conn;
205 }
206 }
207
208 [] GSUP.receive { repeat; }
209
210 /* Client -> GSUP Socket: Normal message */
211 [] GSUP_CLIENT.receive(GSUP_PDU:?) -> value gsup sender vc_conn {
212 /* forward to GSUP socket */
213 GSUP.send(gsup);
214 }
215
216
217 /* Client -> us: procedure call to register expect */
218 [] GSUP_PROC.getcall(GSUPEM_register:{?,?}) -> param(imsi, vc_hdlr) {
219 f_create_expect(imsi, vc_hdlr);
Harald Weltee32ad992018-05-31 22:17:46 +0200220 GSUP_PROC.reply(GSUPEM_register:{imsi, vc_hdlr}) to vc_hdlr;
Harald Welte0e5aad22018-01-21 14:00:41 +0100221 }
222
223 }
224 }
225}
226
227/***********************************************************************
228 * "Expect" Handling (mapping for expected incoming GSUP calls from IUT)
229 ***********************************************************************/
230
231/* data about an expected future incoming connection */
232type record ExpectData {
233 /* destination number based on which we can match it */
234 charstring imsi optional,
235 /* component reference for this connection */
236 GSUP_ConnHdlr vc_conn
237}
238
239/* procedure based port to register for incoming calls */
240signature GSUPEM_register(in charstring imsi, in GSUP_ConnHdlr hdlr);
241
242type port GSUPEM_PROC_PT procedure {
243 inout GSUPEM_register;
244} with { extension "internal" };
245
246/* CreateCallback that can be used as create_cb and will use the expectation table */
247function ExpectedCreateCallback(GSUP_PDU gsup, charstring id)
248runs on GSUP_Emulation_CT return GSUP_ConnHdlr {
249 var GSUP_ConnHdlr ret := null;
250 var charstring imsi;
251 var integer i;
252
253 imsi := hex2str(gsup.ies[0].val.imsi);
254
255 for (i := 0; i < sizeof(GsupExpectTable); i:= i+1) {
256 if (not ispresent(GsupExpectTable[i].imsi)) {
257 continue;
258 }
259 if (imsi == GsupExpectTable[i].imsi) {
260 ret := GsupExpectTable[i].vc_conn;
261 /* release this entry to be used again */
262 GsupExpectTable[i].imsi := omit;
263 GsupExpectTable[i].vc_conn := null;
264 log("Found GsupExpect[", i, "] for ", imsi, " handled at ", ret);
265 /* return the component reference */
266 return ret;
267 }
268 }
269 setverdict(fail, "Couldn't find GsupExpect for incoming imsi ", imsi);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200270 mtc.stop;
Harald Welte0e5aad22018-01-21 14:00:41 +0100271 return ret;
272}
273
274/* server/emulation side function to create expect */
275private function f_create_expect(charstring imsi, GSUP_ConnHdlr hdlr)
276runs on GSUP_Emulation_CT {
277 var integer i;
278 for (i := 0; i < sizeof(GsupExpectTable); i := i+1) {
279 if (not ispresent(GsupExpectTable[i].imsi)) {
280 GsupExpectTable[i].imsi := imsi;
281 GsupExpectTable[i].vc_conn := hdlr;
282 log("Created GsupExpect[", i, "] for ", imsi, " to be handled at ", hdlr);
283 return;
284 }
285 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200286 testcase.stop("No space left in GsupExpectTable");
Harald Welte0e5aad22018-01-21 14:00:41 +0100287}
288
289/* client/conn_hdlr side function to use procedure port to create expect in emulation */
290function f_create_gsup_expect(charstring imsi) runs on GSUP_ConnHdlr {
291 GSUP_PROC.call(GSUPEM_register:{imsi, self}) {
292 [] GSUP_PROC.getreply(GSUPEM_register:{?,?}) {};
293 }
294}
295
296}