blob: a9c445e0153e6828e9eae27362033930fa590632 [file] [log] [blame]
Daniel Willmann19b8d902022-01-05 09:12:34 +01001module RUA_Emulation {
2
3/* RUA_Emulation runs on top of Iuh_Emulation. It multiplexes/demultiplexes
4 * the individuao connections, so there can be separate TTCN-3 components
5 * handling each of the connections (one connection per UE).
6 *
7 * The RUA_Emulation.main() function processes RUA messages from the Iuh stack
8 * via the RUA_PT, and dispatches them to the per-connection components.
9 *
10 * Outbound RUA connections are initiated by sending a FIXME primitive to the
11 * RUA_Emulation component.
12 *
13 * For each new inbound connection, the RuaOps.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 future inbound connection,
18 * it can do so by registering an "expect" with the expected RANAP payload.
19
20 * (C) 2022 by Harald Welte <laforge@gnumonks.org>
21 * All rights reserved.
22 *
23 * Released under the terms of GNU General Public License, Version 2 or
24 * (at your option) any later version.
25 */
26
27import from General_Types all;
28import from Osmocom_Types all;
29
30import from Iuh_Emulation all;
31
32import from RUA_Templates all;
33//import from RUA_Constants all;
34import from RUA_PDU_Descriptions all;
35import from RUA_IEs all;
36
37import from RANAP_PDU_Descriptions all;
38//import from RANAP_Constants all;
39import from RANAP_IEs all;
40import from RANAP_Types all;
41import from RANAP_Templates all;
42
43modulepar {
44 integer mp_max_context_id := hex2int('FFFFFF'H);
45}
46
47
48/* General "base class" component definition, of which specific implementations
49 * derive themselves by means of the "extends" feature */
50type component RUA_ConnHdlr {
51 port RUA_Conn_PT RUA;
52}
53
54/* port between individual per-connection components and this dispatcher */
55type port RUA_Conn_PT message {
56 inout RANAP_PDU,
57 RUA_Conn_Req,
58 RUA_Disc_Req,
59 RUA_Disc_Ind;
60} with { extension "internal" };
61
62type record RUA_Conn_Req {
63 boolean ps_domain,
64 RANAP_PDU ranap
65};
66
67type record RUA_Disc_Req {
68 RANAP_PDU ranap,
69 RUA_IEs.Cause cause
70};
71
72type record RUA_Disc_Ind {
73 RUA_IEs.Cause cause
74};
75
76type bitstring ContextId length(24); // with { variant "FIELDLENGTH(24)" };
77
78/* represents a single RANAP connection over RUA */
79type record ConnectionData {
80 RUA_ConnHdlr comp_ref,
81 RUA_IEs.CN_DomainIndicator domain,
82 integer context_id
83}
84
85type component RUA_Emulation_CT {
86 /* port to the bottom side (Iuh) */
87 port RUA_PT RUA;
88
89 /* ports to the upper side (per-connection components) */
90 port RUA_Conn_PT CLIENT;
91
92 /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
93 var ConnectionData ConnectionTable[16];
94
95 /* pending expected incoming connections */
96 //var ExpectData ExpectTable[8];
97
98 /* tables for mapping inbound unitdata (like paging) */
99 //var ImsiMapping ImsiTable[16];
100
101 /* procedure based port to register for incoming connections */
102 //port RUA_PROC_PT PROC;
103
104 var charstring g_rua_id;
105 var RuaOps g_rua_ops;
106}
107
108type function RanapCreateCallback(ContextId context_id, RUA_IEs.CN_DomainIndicator domain, charstring id)
109runs on RUA_Emulation_CT return RUA_ConnHdlr;
110
111type function RanapUnitdataCallback(RANAP_PDU ranap)
112runs on RUA_Emulation_CT return template RANAP_PDU;
113
114type record RuaOps {
115 RanapCreateCallback create_cb optional,
116 RanapUnitdataCallback unitdata_cb optional
117 //boolean deode_dtap
118 //boolean role_ms
119};
120
121private function f_context_id_known(ContextId context_id)
122runs on RUA_Emulation_CT return boolean {
123 var integer i;
124 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
125 if (ConnectionTable[i].context_id == bit2int(context_id)){
126 return true;
127 }
128 }
129 return false;
130}
131
132private function f_comp_known(RUA_ConnHdlr client)
133runs on RUA_Emulation_CT return boolean {
134 var integer i;
135 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
136 if (ConnectionTable[i].comp_ref == client) {
137 return true;
138 }
139 }
140 return false;
141}
142
143/* resolve connection ID by component reference */
144private function f_context_id_by_comp(RUA_ConnHdlr client)
145runs on RUA_Emulation_CT return ContextId {
146 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
147 if (ConnectionTable[i].comp_ref == client) {
148 return int2bit(ConnectionTable[i].context_id, 24);
149 }
150 }
151 setverdict(fail, "RAN Connection table not found by component ", client);
152 mtc.stop;
153}
154
155/* resolve ConnectionTable index component reference */
156private function f_idx_by_comp(RUA_ConnHdlr client)
157runs on RUA_Emulation_CT return integer {
158 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
159 if (ConnectionTable[i].comp_ref == client) {
160 return i;
161 }
162 }
163 setverdict(fail, "RAN Connection table not found by component ", client);
164 mtc.stop;
165}
166
167private function f_gen_context_id()
168runs on RUA_Emulation_CT return ContextId {
169 var ContextId context_id;
170
171 do {
172 context_id := int2bit(float2int(rnd()*int2float(mp_max_context_id)), 24);
173 } while (f_context_id_known(context_id) == true);
174
175 return context_id;
176}
177
178private function f_conn_table_init()
179runs on RUA_Emulation_CT {
180 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
181 ConnectionTable[i].comp_ref := null;
182 ConnectionTable[i].context_id := -1;
183 }
184/*
185 for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) {
186 ImsiTable[i].comp_ref := null;
187 ImsiTable[i].imsi := omit;
188 ImsiTable[i].tmsi := 'FFFFFFFF'O;
189 }
190*/
191}
192
193private function f_conn_table_add(RUA_ConnHdlr comp_ref, RUA_IEs.CN_DomainIndicator domain, ContextId context_id)
194runs on RUA_Emulation_CT {
195 var integer int_context_id := bit2int(context_id);
196 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
197 if (ConnectionTable[i].context_id == -1) {
198 ConnectionTable[i].comp_ref := comp_ref;
199 ConnectionTable[i].domain := domain;
200 ConnectionTable[i].context_id := int_context_id;
201 log("Added conn table entry ", i, comp_ref, int_context_id);
202 return;
203 }
204 }
205 testcase.stop("RUA Connection table full!");
206}
207
208private function f_conn_table_del(ContextId context_id)
209runs on RUA_Emulation_CT {
210 var integer int_context_id := bit2int(context_id);
211 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
212 if (ConnectionTable[i].context_id == int_context_id) {
213 log("Deleted conn table entry ", i,
214 ConnectionTable[i].comp_ref, int_context_id);
215 ConnectionTable[i].context_id := -1;
216 return
217 }
218 }
219 setverdict(fail, "RUA Connection table attempt to delete non-existant ", int_context_id);
220 mtc.stop;
221}
222
223
224/* resolve component reference by connection ID */
225private function f_comp_by_context_id(ContextId context_id)
226runs on RUA_Emulation_CT return RUA_ConnHdlr {
227 var integer int_context_id := bit2int(context_id);
228 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
229 if (ConnectionTable[i].context_id == int_context_id) {
230 return ConnectionTable[i].comp_ref;
231 }
232 }
233 setverdict(fail, "RUA Connection table not found by RUA Context ID ", int_context_id);
234 mtc.stop;
235}
236
237private function CommonRanapUnitdataCallback(RANAP_PDU ranap)
238runs on RUA_Emulation_CT return template RANAP_PDU {
239 /* TODO: paging */
240 return g_rua_ops.unitdata_cb.apply(ranap);
241}
242
243private function f_handle_userData_RANAP(RUA_ConnHdlr client, RANAP_PDU ranap)
244runs on RUA_Emulation_CT {
245 /* TODO: L3 decoding, if requested */
246 CLIENT.send(ranap) to client;
247}
248
249
250private altstep as_reset_ack() runs on RUA_Emulation_CT {
251 var RUA_PDU rua_clt;
252 [] RUA.receive(tr_RUA_ConnectionlessTransfer(decmatch tr_RANAP_Reset)) -> value rua_clt {
253 var RANAP_PDU rx := dec_RANAP_PDU(rua_clt.initiatingMessage.value_.connectionlessTransfer.protocolIEs[0].value_.rANAP_Message);
254 var RANAP_IEs.CN_DomainIndicator dom;
255 dom := rx.initiatingMessage.value_.Reset.protocolIEs[1].value_.cN_DomainIndicator;
256 RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(valueof(ts_RANAP_ResetAck(dom)))));
257 }
258}
259
260private altstep as_main_rua() runs on RUA_Emulation_CT {
261 var RANAP_PDU ranap;
262 var RUA_PDU rua;
263 var octetstring ranap_enc;
264 var ContextId context_id;
265 var RUA_IEs.CN_DomainIndicator domain_ind;
266 var RUA_ConnHdlr vc_conn;
267 var RUA_Conn_Req creq;
268 var RUA_Disc_Req dreq;
269 var RUA_IEs.Cause cause;
270
271 /* RUA -> Client: UNIT-DATA (connectionless RUA) from CN */
272 [] RUA.receive(tr_RUA_ConnectionlessTransfer) -> value rua {
273 ranap := dec_RANAP_PDU(rua.initiatingMessage.value_.connectionlessTransfer.protocolIEs[0].value_.rANAP_Message);
274 var template RANAP_PDU resp;
275 resp := CommonRanapUnitdataCallback(ranap);
276 if (isvalue(resp)) {
277 RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(valueof(resp))));
278 }
279 }
280
281 /* RUA -> Client: new connection from CN */
282 [] RUA.receive(tr_RUA_Connect) -> value rua {
283 domain_ind := rua.initiatingMessage.value_.connect_.protocolIEs[0].value_.cN_DomainIndicator;
284 context_id := rua.initiatingMessage.value_.connect_.protocolIEs[1].value_.context_ID;
285 ranap_enc := rua.initiatingMessage.value_.connect_.protocolIEs[3].value_.rANAP_Message;
286 ranap := dec_RANAP_PDU(ranap_enc);
287 vc_conn := g_rua_ops.create_cb.apply(context_id, domain_ind, g_rua_id);
288 /* store mapping between client components and RUA contextId */
289 f_conn_table_add(vc_conn, domain_ind, context_id);
290 /* TODO: notify user about incoming connection? */
291 /* handle user payload */
292 f_handle_userData_RANAP(vc_conn, ranap);
293 }
294
295 /* RUA -> Client: connection-oriented data in existing connection */
296 [] RUA.receive(tr_RUA_DirectTransfer) -> value rua {
297 context_id := rua.initiatingMessage.value_.directTransfer.protocolIEs[1].value_.context_ID;
298 vc_conn := f_comp_by_context_id(context_id);
299 ranap_enc := rua.initiatingMessage.value_.directTransfer.protocolIEs[2].value_.rANAP_Message;
300 f_handle_userData_RANAP(vc_conn, dec_RANAP_PDU(ranap_enc));
301 }
302
303 /* RUA -> Client: disconnect of an existing connection */
Neels Hofmeyr8f5c80d2023-06-10 00:22:51 +0200304 [] RUA.receive(tr_RUA_Disconnect_opt_ranap) -> value rua {
Daniel Willmann19b8d902022-01-05 09:12:34 +0100305 cause := rua.initiatingMessage.value_.disconnect_.protocolIEs[2].value_.cause;
306 context_id := rua.initiatingMessage.value_.disconnect_.protocolIEs[1].value_.context_ID;
307 vc_conn := f_comp_by_context_id(context_id);
308 /* send contained RANAP message to user */
Neels Hofmeyr8f5c80d2023-06-10 00:22:51 +0200309 if (lengthof(rua.initiatingMessage.value_.disconnect_.protocolIEs) > 3) {
310 ranap_enc := rua.initiatingMessage.value_.disconnect_.protocolIEs[3].value_.rANAP_Message;
311 f_handle_userData_RANAP(vc_conn, dec_RANAP_PDU(ranap_enc));
312 }
Daniel Willmann19b8d902022-01-05 09:12:34 +0100313 /* notify user of disconnect */
Neels Hofmeyr02040fd2023-06-10 00:22:51 +0200314 if (CLIENT.checkstate("Connected")) {
315 CLIENT.send(RUA_Disc_Ind:{cause});
316 }
Daniel Willmann19b8d902022-01-05 09:12:34 +0100317 f_conn_table_del(context_id);
318 }
319
320 /* RANAP from client through an existing RANAP connection */
321 [] CLIENT.receive(RANAP_PDU:?) -> value ranap sender vc_conn {
322 var integer idx := f_idx_by_comp(vc_conn);
323 context_id := int2bit(ConnectionTable[idx].context_id, 24);
324 domain_ind := ConnectionTable[idx].domain;
325 RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_RANAP_PDU(ranap)));
326 }
327
328 /* Disconnect request from client */
329 [] CLIENT.receive(RUA_Disc_Req:?) -> value dreq sender vc_conn {
330 var octetstring enc_ranap := enc_RANAP_PDU(dreq.ranap);
331 var integer idx := f_idx_by_comp(vc_conn);
332 context_id := int2bit(ConnectionTable[idx].context_id, 24);
333 domain_ind := ConnectionTable[idx].domain;
334 RUA.send(ts_RUA_Disconnect(domain_ind, context_id, dreq.cause, enc_ranap));
335 f_conn_table_del(context_id);
336 }
337
338 /* RANAP from client, for a new RANAP connection */
339 [] CLIENT.receive(RUA_Conn_Req:?) -> value creq sender vc_conn {
340 var octetstring enc_ranap := enc_RANAP_PDU(creq.ranap);
341
342 if (f_comp_known(vc_conn) == false) {
343 /* unknown client, create new connection */
344 context_id := f_gen_context_id();
345 if (creq.ps_domain) {
346 domain_ind := ps_domain;
347 } else {
348 domain_ind := cs_domain;
349 }
350
351 f_conn_table_add(vc_conn, domain_ind, context_id);
352 RUA.send(ts_RUA_Connect(domain_ind, context_id, normal_call, enc_ranap));
353 } else {
354 /* known client, send via existing component */
355 context_id := f_context_id_by_comp(vc_conn);
356 RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_ranap));
357 }
358 }
359
360}
361
362
363
364function f_ranap_reset(RANAP_IEs.CN_DomainIndicator dom) runs on RUA_Emulation_CT {
365 timer T := 5.0;
366
367 var RANAP_PDU tx := valueof(ts_RANAP_Reset(ts_RanapCause_om_intervention,dom));
368 RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(tx)));
369 T.start;
370 alt {
371 [] RUA.receive(tr_RUA_ConnectionlessTransfer(decmatch tr_RANAP_ResetAck)) {
Oliver Smith23e192e2023-02-13 15:00:46 +0100372 log("RUA-RANAP: Received RESET-ACK in response to RESET, we're reay to go!");
Daniel Willmann19b8d902022-01-05 09:12:34 +0100373 }
374 [] as_reset_ack();
375 [] RUA.receive { repeat; }
376 [] T.timeout {
377 setverdict(fail, "RUA-RANAP: Timeout waiting for RESET-ACK after sending RESET");
378 mtc.stop;
379 }
380 }
381}
382
383function main(RuaOps ops, charstring id) runs on RUA_Emulation_CT {
384 g_rua_id := id;
385 g_rua_ops := ops;
386 f_conn_table_init();
387 //f_expect_table_init();
388
389 while (true) {
390 alt {
391 [] as_main_rua();
392
393 /*
394 [] PROC.getcall(RUA_Register:{?,?}) -> param(l3_info, vc_hdlr) {
395 f_create_expect(l3_info, vc_hdlr);
396 PROC.reply(RUA_register:{l3_info, vc_hdlr}) to vc_hdlr;
397 }
398 */
399 }
400 }
401}
402
403
404}