Daniel Willmann | 19b8d90 | 2022-01-05 09:12:34 +0100 | [diff] [blame] | 1 | module 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 | |
| 27 | import from General_Types all; |
| 28 | import from Osmocom_Types all; |
| 29 | |
| 30 | import from Iuh_Emulation all; |
| 31 | |
| 32 | import from RUA_Templates all; |
| 33 | //import from RUA_Constants all; |
| 34 | import from RUA_PDU_Descriptions all; |
| 35 | import from RUA_IEs all; |
| 36 | |
| 37 | import from RANAP_PDU_Descriptions all; |
| 38 | //import from RANAP_Constants all; |
| 39 | import from RANAP_IEs all; |
| 40 | import from RANAP_Types all; |
| 41 | import from RANAP_Templates all; |
| 42 | |
| 43 | modulepar { |
| 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 */ |
| 50 | type component RUA_ConnHdlr { |
| 51 | port RUA_Conn_PT RUA; |
| 52 | } |
| 53 | |
| 54 | /* port between individual per-connection components and this dispatcher */ |
| 55 | type 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 | |
| 62 | type record RUA_Conn_Req { |
| 63 | boolean ps_domain, |
| 64 | RANAP_PDU ranap |
| 65 | }; |
| 66 | |
| 67 | type record RUA_Disc_Req { |
| 68 | RANAP_PDU ranap, |
| 69 | RUA_IEs.Cause cause |
| 70 | }; |
| 71 | |
| 72 | type record RUA_Disc_Ind { |
| 73 | RUA_IEs.Cause cause |
| 74 | }; |
| 75 | |
| 76 | type bitstring ContextId length(24); // with { variant "FIELDLENGTH(24)" }; |
| 77 | |
| 78 | /* represents a single RANAP connection over RUA */ |
| 79 | type record ConnectionData { |
| 80 | RUA_ConnHdlr comp_ref, |
| 81 | RUA_IEs.CN_DomainIndicator domain, |
| 82 | integer context_id |
| 83 | } |
| 84 | |
| 85 | type 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 | |
| 108 | type function RanapCreateCallback(ContextId context_id, RUA_IEs.CN_DomainIndicator domain, charstring id) |
| 109 | runs on RUA_Emulation_CT return RUA_ConnHdlr; |
| 110 | |
| 111 | type function RanapUnitdataCallback(RANAP_PDU ranap) |
| 112 | runs on RUA_Emulation_CT return template RANAP_PDU; |
| 113 | |
| 114 | type record RuaOps { |
| 115 | RanapCreateCallback create_cb optional, |
| 116 | RanapUnitdataCallback unitdata_cb optional |
| 117 | //boolean deode_dtap |
| 118 | //boolean role_ms |
| 119 | }; |
| 120 | |
| 121 | private function f_context_id_known(ContextId context_id) |
| 122 | runs 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 | |
| 132 | private function f_comp_known(RUA_ConnHdlr client) |
| 133 | runs 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 */ |
| 144 | private function f_context_id_by_comp(RUA_ConnHdlr client) |
| 145 | runs 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 */ |
| 156 | private function f_idx_by_comp(RUA_ConnHdlr client) |
| 157 | runs 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 | |
| 167 | private function f_gen_context_id() |
| 168 | runs 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 | |
| 178 | private function f_conn_table_init() |
| 179 | runs 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 | |
| 193 | private function f_conn_table_add(RUA_ConnHdlr comp_ref, RUA_IEs.CN_DomainIndicator domain, ContextId context_id) |
| 194 | runs 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 | |
| 208 | private function f_conn_table_del(ContextId context_id) |
| 209 | runs 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 */ |
| 225 | private function f_comp_by_context_id(ContextId context_id) |
| 226 | runs 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 | |
| 237 | private function CommonRanapUnitdataCallback(RANAP_PDU ranap) |
| 238 | runs on RUA_Emulation_CT return template RANAP_PDU { |
| 239 | /* TODO: paging */ |
| 240 | return g_rua_ops.unitdata_cb.apply(ranap); |
| 241 | } |
| 242 | |
| 243 | private function f_handle_userData_RANAP(RUA_ConnHdlr client, RANAP_PDU ranap) |
| 244 | runs on RUA_Emulation_CT { |
| 245 | /* TODO: L3 decoding, if requested */ |
| 246 | CLIENT.send(ranap) to client; |
| 247 | } |
| 248 | |
| 249 | |
| 250 | private 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 | |
| 260 | private 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 */ |
| 304 | [] RUA.receive(tr_RUA_Disconnect) -> value rua { |
| 305 | 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 */ |
| 309 | ranap_enc := rua.initiatingMessage.value_.disconnect_.protocolIEs[3].value_.rANAP_Message; |
| 310 | f_handle_userData_RANAP(vc_conn, dec_RANAP_PDU(ranap_enc)); |
| 311 | /* notify user of disconnect */ |
| 312 | CLIENT.send(RUA_Disc_Ind:{cause}); |
| 313 | f_conn_table_del(context_id); |
| 314 | } |
| 315 | |
| 316 | /* RANAP from client through an existing RANAP connection */ |
| 317 | [] CLIENT.receive(RANAP_PDU:?) -> value ranap sender vc_conn { |
| 318 | var integer idx := f_idx_by_comp(vc_conn); |
| 319 | context_id := int2bit(ConnectionTable[idx].context_id, 24); |
| 320 | domain_ind := ConnectionTable[idx].domain; |
| 321 | RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_RANAP_PDU(ranap))); |
| 322 | } |
| 323 | |
| 324 | /* Disconnect request from client */ |
| 325 | [] CLIENT.receive(RUA_Disc_Req:?) -> value dreq sender vc_conn { |
| 326 | var octetstring enc_ranap := enc_RANAP_PDU(dreq.ranap); |
| 327 | var integer idx := f_idx_by_comp(vc_conn); |
| 328 | context_id := int2bit(ConnectionTable[idx].context_id, 24); |
| 329 | domain_ind := ConnectionTable[idx].domain; |
| 330 | RUA.send(ts_RUA_Disconnect(domain_ind, context_id, dreq.cause, enc_ranap)); |
| 331 | f_conn_table_del(context_id); |
| 332 | } |
| 333 | |
| 334 | /* RANAP from client, for a new RANAP connection */ |
| 335 | [] CLIENT.receive(RUA_Conn_Req:?) -> value creq sender vc_conn { |
| 336 | var octetstring enc_ranap := enc_RANAP_PDU(creq.ranap); |
| 337 | |
| 338 | if (f_comp_known(vc_conn) == false) { |
| 339 | /* unknown client, create new connection */ |
| 340 | context_id := f_gen_context_id(); |
| 341 | if (creq.ps_domain) { |
| 342 | domain_ind := ps_domain; |
| 343 | } else { |
| 344 | domain_ind := cs_domain; |
| 345 | } |
| 346 | |
| 347 | f_conn_table_add(vc_conn, domain_ind, context_id); |
| 348 | RUA.send(ts_RUA_Connect(domain_ind, context_id, normal_call, enc_ranap)); |
| 349 | } else { |
| 350 | /* known client, send via existing component */ |
| 351 | context_id := f_context_id_by_comp(vc_conn); |
| 352 | RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_ranap)); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | } |
| 357 | |
| 358 | |
| 359 | |
| 360 | function f_ranap_reset(RANAP_IEs.CN_DomainIndicator dom) runs on RUA_Emulation_CT { |
| 361 | timer T := 5.0; |
| 362 | |
| 363 | var RANAP_PDU tx := valueof(ts_RANAP_Reset(ts_RanapCause_om_intervention,dom)); |
| 364 | RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(tx))); |
| 365 | T.start; |
| 366 | alt { |
| 367 | [] RUA.receive(tr_RUA_ConnectionlessTransfer(decmatch tr_RANAP_ResetAck)) { |
Oliver Smith | 23e192e | 2023-02-13 15:00:46 +0100 | [diff] [blame] | 368 | log("RUA-RANAP: Received RESET-ACK in response to RESET, we're reay to go!"); |
Daniel Willmann | 19b8d90 | 2022-01-05 09:12:34 +0100 | [diff] [blame] | 369 | } |
| 370 | [] as_reset_ack(); |
| 371 | [] RUA.receive { repeat; } |
| 372 | [] T.timeout { |
| 373 | setverdict(fail, "RUA-RANAP: Timeout waiting for RESET-ACK after sending RESET"); |
| 374 | mtc.stop; |
| 375 | } |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | function main(RuaOps ops, charstring id) runs on RUA_Emulation_CT { |
| 380 | g_rua_id := id; |
| 381 | g_rua_ops := ops; |
| 382 | f_conn_table_init(); |
| 383 | //f_expect_table_init(); |
| 384 | |
| 385 | while (true) { |
| 386 | alt { |
| 387 | [] as_main_rua(); |
| 388 | |
| 389 | /* |
| 390 | [] PROC.getcall(RUA_Register:{?,?}) -> param(l3_info, vc_hdlr) { |
| 391 | f_create_expect(l3_info, vc_hdlr); |
| 392 | PROC.reply(RUA_register:{l3_info, vc_hdlr}) to vc_hdlr; |
| 393 | } |
| 394 | */ |
| 395 | } |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | |
| 400 | } |