blob: de5b7f38c69637954cfd2b0639299a8885e56047 [file] [log] [blame]
Harald Welte6811d102019-04-14 22:23:14 +02001module RAN_Emulation {
Harald Welte365f4ed2017-11-23 00:00:43 +01002
Harald Welte6811d102019-04-14 22:23:14 +02003/* RAN Emulation, runs on top of BSSAP_CodecPort. It multiplexes/demultiplexes
Harald Welte35bb7162018-01-03 21:07:52 +01004 * the individual connections, so there can be separate TTCN-3 components handling
5 * each of the connections.
6 *
Harald Welte6811d102019-04-14 22:23:14 +02007 * The RAN_Emulation.main() function processes SCCP primitives from the SCCP
Harald Welte35bb7162018-01-03 21:07:52 +01008 * stack via the BSSAP_CodecPort, and dispatches them to the per-connection components.
9 *
10 * Outbound BSSAP/SCCP connections are initiated by sending a BSSAP_Conn_Req primitive
Harald Welte6811d102019-04-14 22:23:14 +020011 * to the component running the RAN_Emulation.main() function.
Harald Welte35bb7162018-01-03 21:07:52 +010012 *
Harald Welte6811d102019-04-14 22:23:14 +020013 * For each new inbound connections, the RanOps.create_cb() is called. It can create
Harald Welte35bb7162018-01-03 21:07:52 +010014 * 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 connection, it can
18 * do so by registering an "expect" with the expected Layer 3 (DTAP) payload. This is e.g. useful
19 * if you are simulating BTS + MSC, and first trigger a connection from BTS/RSL side in a
20 * component which then subsequently should also handle the MSC emulation.
21 *
Harald Welte6811d102019-04-14 22:23:14 +020022 * Inbound Unit Data messages (such as are dispatched to the RanOps.unitdata_cb() callback,
Harald Welte35bb7162018-01-03 21:07:52 +010023 * which is registered with an argument to the main() function below.
24 *
Harald Welte2fce7882019-04-15 11:48:05 +020025 * (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
Harald Welte35bb7162018-01-03 21:07:52 +010026 * All rights reserved.
27 *
28 * Released under the terms of GNU General Public License, Version 2 or
29 * (at your option) any later version.
30 */
31
32
Harald Welte0b476062018-01-21 19:07:32 +010033import from General_Types all;
Harald Welte9dadc522018-02-06 13:56:04 +010034import from Osmocom_Types all;
Harald Welteb3414b22017-11-23 18:22:10 +010035import from SCCP_Emulation all;
Harald Welte365f4ed2017-11-23 00:00:43 +010036import from SCCPasp_Types all;
Harald Welte2fce7882019-04-15 11:48:05 +020037import from IPA_Emulation all;
38import from MobileL3_Types all;
Pau Espin Pedrol4d0e5e52019-06-07 19:38:28 +020039import from Misc_Helpers all;
Harald Welte2fce7882019-04-15 11:48:05 +020040
41#ifdef RAN_EMULATION_BSSAP
Harald Welte365f4ed2017-11-23 00:00:43 +010042import from BSSAP_Types all;
Harald Welte004f5fb2017-12-16 17:54:40 +010043import from BSSAP_CodecPort all;
Harald Welte365f4ed2017-11-23 00:00:43 +010044import from BSSMAP_Templates all;
Harald Welte2fce7882019-04-15 11:48:05 +020045#endif
46
47#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +010048import from MGCP_Types all;
49import from MGCP_Templates all;
Harald Welte2fce7882019-04-15 11:48:05 +020050#endif
Harald Welte365f4ed2017-11-23 00:00:43 +010051
Pau Espin Pedrol4d0e5e52019-06-07 19:38:28 +020052#ifdef RAN_EMULATION_CTRL
53import from Osmocom_CTRL_Types all;
54import from Osmocom_CTRL_Adapter all;
55#endif
56
Harald Welte5b027622019-04-14 23:40:17 +020057#ifdef RAN_EMULATION_RANAP
58import from RANAP_CodecPort all;
59import from RANAP_PDU_Descriptions all;
60import from RANAP_Constants all;
61import from RANAP_IEs all;
62import from RANAP_Templates all;
63#endif
64
Harald Welte365f4ed2017-11-23 00:00:43 +010065/* General "base class" component definition, of which specific implementations
66 * derive themselves by means of the "extends" feature */
Harald Welte6811d102019-04-14 22:23:14 +020067type component RAN_ConnHdlr {
Harald Welte365f4ed2017-11-23 00:00:43 +010068 /* port towards MSC Emulator core / SCCP connection dispatchar */
Harald Welte6811d102019-04-14 22:23:14 +020069 port RAN_Conn_PT BSSAP;
Harald Welteca519982018-01-21 19:28:26 +010070 /* procedure based port to register for incoming connections */
Harald Welte6811d102019-04-14 22:23:14 +020071 port RAN_PROC_PT BSSAP_PROC;
Harald Welte365f4ed2017-11-23 00:00:43 +010072}
73
74/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */
Harald Welte6811d102019-04-14 22:23:14 +020075type enumerated RAN_Conn_Prim {
Harald Welte365f4ed2017-11-23 00:00:43 +010076 /* SCCP tell us that connection was released */
77 MSC_CONN_PRIM_DISC_IND,
78 /* we tell SCCP to release connection */
Harald Welte71b69332018-01-21 20:43:53 +010079 MSC_CONN_PRIM_DISC_REQ,
80 /* Connection confirmed indication */
81 MSC_CONN_PRIM_CONF_IND
Harald Welte365f4ed2017-11-23 00:00:43 +010082}
83
Pau Espin Pedrolc6b78ff2019-06-06 15:58:17 +020084type enumerated RAN_Transport {
85 BSSAP_TRANSPORT_AoIP, /* 3GPP AoIP: SCCP over M3UA over SCTP */
86 BSSAP_TRANSPORT_SCCPlite_SERVER, /* SCCPlite: SCCP over IPA over TCP */
87 BSSAP_TRANSPORT_SCCPlite_CLIENT, /* SCCPlite: SCCP over IPA over TCP */
88 RANAP_TRANSPORT_IuCS /* 3GPP IuCS: SCCP over M3UA over SCTP */
89};
90
Harald Welte0b476062018-01-21 19:07:32 +010091/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */
92type record PDU_DTAP_MO {
93 OCT1 dlci optional,
Daniel Willmann92f66272018-02-06 15:50:52 +010094 boolean skip_seq_patching optional,
Harald Welte0b476062018-01-21 19:07:32 +010095 PDU_ML3_MS_NW dtap
96}
97
98/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */
99type record PDU_DTAP_MT {
100 OCT1 dlci optional,
101 PDU_ML3_NW_MS dtap
102}
103
104template PDU_DTAP_MT ts_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := omit) := {
105 dlci := dlci,
106 dtap := dtap
107}
108
Daniel Willmann92f66272018-02-06 15:50:52 +0100109template PDU_DTAP_MO ts_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := '00'O, boolean skip_seq_patching := false) := {
Harald Welte0b476062018-01-21 19:07:32 +0100110 dlci := dlci,
Daniel Willmann92f66272018-02-06 15:50:52 +0100111 skip_seq_patching := skip_seq_patching,
Harald Welte0b476062018-01-21 19:07:32 +0100112 dtap := dtap
113}
114
115template PDU_DTAP_MT tr_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := *) := {
116 dlci := dlci,
117 dtap := dtap
118}
119
120template PDU_DTAP_MO tr_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := *) := {
121 dlci := dlci,
Harald Welte930d0a72018-03-22 22:08:40 +0100122 skip_seq_patching := ?,
Harald Welte0b476062018-01-21 19:07:32 +0100123 dtap := dtap
124}
125
Harald Welte365f4ed2017-11-23 00:00:43 +0100126/* port between individual per-connection components and this dispatcher */
Harald Welte6811d102019-04-14 22:23:14 +0200127type port RAN_Conn_PT message {
Harald Welte2fce7882019-04-15 11:48:05 +0200128 inout
129#ifdef RAN_EMULATION_BSSAP
130 PDU_BSSAP,
Harald Weltea4ca4462018-02-09 00:17:14 +0100131 /* Client requests us to create SCCP Connection */
132 BSSAP_Conn_Req,
Harald Welte2fce7882019-04-15 11:48:05 +0200133#endif
Harald Welte5b027622019-04-14 23:40:17 +0200134#ifdef RAN_EMULATION_RANAP
135 RANAP_PDU,
136 /* Client requests us to create SCCP Connection */
137 RANAP_Conn_Req,
138#endif
Harald Welte2fce7882019-04-15 11:48:05 +0200139#ifdef RAN_EMULATION_MGCP
Harald Weltea4ca4462018-02-09 00:17:14 +0100140 /* MGCP, only used for IPA SCCPlite (MGCP in IPA mux) */
Harald Welte2fce7882019-04-15 11:48:05 +0200141 MgcpCommand, MgcpResponse,
142#endif
143 /* direct DTAP messages from/to clients */
144 PDU_DTAP_MO, PDU_DTAP_MT,
145 /* misc indications / requests between SCCP and client */
146 RAN_Conn_Prim;
Harald Welte365f4ed2017-11-23 00:00:43 +0100147} with { extension "internal" };
148
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200149type uint2_t N_Sd_Array[4];
Harald Welte365f4ed2017-11-23 00:00:43 +0100150
151/* represents a single BSSAP connection over SCCP */
152type record ConnectionData {
153 /* reference to the instance of the per-connection component */
Harald Welte6811d102019-04-14 22:23:14 +0200154 RAN_ConnHdlr comp_ref,
Harald Weltec82eef42017-11-24 20:40:12 +0100155 integer sccp_conn_id,
Harald Welte2fce7882019-04-15 11:48:05 +0200156#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +0100157 /* most recent MGCP transaction ID (Used on MSC side) */
158 MgcpTransId mgcp_trans_id optional,
Harald Welte2fce7882019-04-15 11:48:05 +0200159#endif
Harald Weltec82eef42017-11-24 20:40:12 +0100160 /* CIC that has been used for voice of this channel (BSC side) */
Harald Welte9dadc522018-02-06 13:56:04 +0100161 integer cic optional,
162 /* array of N(SD) values for MO DTAP messages, indexed by discriminator */
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200163 N_Sd_Array n_sd
Harald Welte365f4ed2017-11-23 00:00:43 +0100164}
165
Harald Welte17d21152018-01-27 00:47:11 +0100166type record ImsiMapping {
Harald Welte6811d102019-04-14 22:23:14 +0200167 RAN_ConnHdlr comp_ref,
Harald Welte17d21152018-01-27 00:47:11 +0100168 hexstring imsi optional,
169 OCT4 tmsi
170}
171
Harald Welte6811d102019-04-14 22:23:14 +0200172type component RAN_Emulation_CT {
Harald Welte2fce7882019-04-15 11:48:05 +0200173 /* SCCP ports on the bottom side, using ASP primitives */
174#ifdef RAN_EMULATION_BSSAP
Harald Welte004f5fb2017-12-16 17:54:40 +0100175 port BSSAP_CODEC_PT BSSAP;
Harald Welte2fce7882019-04-15 11:48:05 +0200176#endif
Harald Welte5b027622019-04-14 23:40:17 +0200177#ifdef RAN_EMULATION_RANAP
178 port RANAP_CODEC_PT RANAP;
179#endif
Harald Welte365f4ed2017-11-23 00:00:43 +0100180 /* BSSAP port to the per-connection clients */
Harald Welte6811d102019-04-14 22:23:14 +0200181 port RAN_Conn_PT CLIENT;
Harald Welte2fce7882019-04-15 11:48:05 +0200182#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +0100183 /* MGCP port */
184 port IPA_MGCP_PT MGCP;
Harald Welte2fce7882019-04-15 11:48:05 +0200185#endif
Pau Espin Pedrol4d0e5e52019-06-07 19:38:28 +0200186#ifdef RAN_EMULATION_CTRL
187 /* CTRL port */
188 port IPA_CTRL_PT CTRL;
189 port IPA_CTRL_PT CTRL_CLIENT;
190#endif
Harald Welte365f4ed2017-11-23 00:00:43 +0100191
192 /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
193 var ConnectionData ConnectionTable[16];
Harald Welte66fecd42017-11-24 23:53:23 +0100194
Harald Welte624f9632017-12-16 19:26:04 +0100195 /* pending expected incoming connections */
196 var ExpectData ExpectTable[8];
Harald Welte17d21152018-01-27 00:47:11 +0100197
198 /* tables for mapping inbound unitdata (like paging) */
199 var ImsiMapping ImsiTable[16];
200
Harald Welte624f9632017-12-16 19:26:04 +0100201 /* procedure based port to register for incoming connections */
Harald Welte6811d102019-04-14 22:23:14 +0200202 port RAN_PROC_PT PROC;
Harald Welte624f9632017-12-16 19:26:04 +0100203
Harald Welte6811d102019-04-14 22:23:14 +0200204 var charstring g_ran_id;
Harald Welte66fecd42017-11-24 23:53:23 +0100205 var integer g_next_e1_ts := 1;
Harald Welte6811d102019-04-14 22:23:14 +0200206 var RanOps g_ran_ops;
Harald Welte365f4ed2017-11-23 00:00:43 +0100207};
208
Harald Welteb3414b22017-11-23 18:22:10 +0100209private function f_conn_id_known(integer sccp_conn_id)
Harald Welte6811d102019-04-14 22:23:14 +0200210runs on RAN_Emulation_CT return boolean {
Harald Welteb3414b22017-11-23 18:22:10 +0100211 var integer i;
212 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
213 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id){
214 return true;
215 }
216 }
217 return false;
218}
219
Harald Welte6811d102019-04-14 22:23:14 +0200220private function f_comp_known(RAN_ConnHdlr client)
221runs on RAN_Emulation_CT return boolean {
Harald Welteb3414b22017-11-23 18:22:10 +0100222 var integer i;
223 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
224 if (ConnectionTable[i].comp_ref == client) {
225 return true;
226 }
227 }
228 return false;
229}
Harald Welte365f4ed2017-11-23 00:00:43 +0100230
Harald Weltee98bb2e2017-11-29 12:09:48 +0100231private function f_cic_known(integer cic)
Harald Welte6811d102019-04-14 22:23:14 +0200232runs on RAN_Emulation_CT return boolean {
Harald Weltee98bb2e2017-11-29 12:09:48 +0100233 var integer i;
234 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
235 if (ConnectionTable[i].cic == cic) {
236 return true;
237 }
238 }
239 return false;
240}
241
Harald Welte365f4ed2017-11-23 00:00:43 +0100242/* resolve component reference by connection ID */
243private function f_comp_by_conn_id(integer sccp_conn_id)
Harald Welte6811d102019-04-14 22:23:14 +0200244runs on RAN_Emulation_CT return RAN_ConnHdlr {
Harald Welte365f4ed2017-11-23 00:00:43 +0100245 var integer i;
246 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
247 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
248 return ConnectionTable[i].comp_ref;
249 }
250 }
Harald Welte6811d102019-04-14 22:23:14 +0200251 setverdict(fail, "RAN Connection table not found by SCCP Connection ID ", sccp_conn_id);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200252 mtc.stop;
Harald Welte365f4ed2017-11-23 00:00:43 +0100253}
254
Harald Welte2fce7882019-04-15 11:48:05 +0200255
256#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +0100257/* resolve component reference by CIC */
258private function f_comp_by_mgcp_tid(MgcpTransId tid)
Harald Welte6811d102019-04-14 22:23:14 +0200259runs on RAN_Emulation_CT return RAN_ConnHdlr {
Harald Weltec82eef42017-11-24 20:40:12 +0100260 var integer i;
261 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
262 if (ConnectionTable[i].mgcp_trans_id == tid) {
263 return ConnectionTable[i].comp_ref;
264 }
265 }
Harald Welte6811d102019-04-14 22:23:14 +0200266 setverdict(fail, "RAN Connection table not found by MGCP Transaction ID ", tid);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200267 mtc.stop;
Harald Weltec82eef42017-11-24 20:40:12 +0100268}
269
Harald Welte6811d102019-04-14 22:23:14 +0200270private function f_comp_store_mgcp_tid(RAN_ConnHdlr client, MgcpTransId tid)
271runs on RAN_Emulation_CT {
Harald Weltec82eef42017-11-24 20:40:12 +0100272 var integer i;
273 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
274 if (ConnectionTable[i].comp_ref == client) {
275 ConnectionTable[i].mgcp_trans_id := tid;
276 return;
277 }
278 }
Harald Welte6811d102019-04-14 22:23:14 +0200279 setverdict(fail, "RAN Connection table not found by component ", client);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200280 mtc.stop;
Harald Weltec82eef42017-11-24 20:40:12 +0100281}
Harald Welte2fce7882019-04-15 11:48:05 +0200282#endif
Harald Weltec82eef42017-11-24 20:40:12 +0100283
284private function f_comp_by_cic(integer cic)
Harald Welte6811d102019-04-14 22:23:14 +0200285runs on RAN_Emulation_CT return RAN_ConnHdlr {
Harald Weltec82eef42017-11-24 20:40:12 +0100286 var integer i;
287 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
288 if (ConnectionTable[i].cic == cic) {
289 return ConnectionTable[i].comp_ref;
290 }
291 }
Harald Welte6811d102019-04-14 22:23:14 +0200292 setverdict(fail, "RAN Connection table not found by CIC ", cic);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200293 mtc.stop;
Harald Weltec82eef42017-11-24 20:40:12 +0100294}
295
Harald Welte6811d102019-04-14 22:23:14 +0200296private function f_comp_store_cic(RAN_ConnHdlr client, integer cic)
297runs on RAN_Emulation_CT {
Harald Weltec82eef42017-11-24 20:40:12 +0100298 var integer i;
299 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
300 if (ConnectionTable[i].comp_ref == client) {
301 ConnectionTable[i].cic := cic;
302 return;
303 }
304 }
Harald Welte6811d102019-04-14 22:23:14 +0200305 setverdict(fail, "RAN Connection table not found by component ", client);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200306 mtc.stop;
Harald Weltec82eef42017-11-24 20:40:12 +0100307}
308
Harald Welte365f4ed2017-11-23 00:00:43 +0100309/* resolve connection ID by component reference */
Harald Welte6811d102019-04-14 22:23:14 +0200310private function f_conn_id_by_comp(RAN_ConnHdlr client)
311runs on RAN_Emulation_CT return integer {
Harald Welte365f4ed2017-11-23 00:00:43 +0100312 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
313 if (ConnectionTable[i].comp_ref == client) {
314 return ConnectionTable[i].sccp_conn_id;
315 }
316 }
Harald Welte6811d102019-04-14 22:23:14 +0200317 setverdict(fail, "RAN Connection table not found by component ", client);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200318 mtc.stop;
Harald Welte365f4ed2017-11-23 00:00:43 +0100319}
320
Harald Welte9dadc522018-02-06 13:56:04 +0100321/* resolve ConnectionTable index component reference */
Harald Welte6811d102019-04-14 22:23:14 +0200322private function f_idx_by_comp(RAN_ConnHdlr client)
323runs on RAN_Emulation_CT return integer {
Harald Welte9dadc522018-02-06 13:56:04 +0100324 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
325 if (ConnectionTable[i].comp_ref == client) {
326 return i;
327 }
328 }
Harald Welte6811d102019-04-14 22:23:14 +0200329 setverdict(fail, "RAN Connection table not found by component ", client);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200330 mtc.stop;
Harald Welte9dadc522018-02-06 13:56:04 +0100331}
332
Harald Welteb3414b22017-11-23 18:22:10 +0100333private function f_gen_conn_id()
Harald Welte6811d102019-04-14 22:23:14 +0200334runs on RAN_Emulation_CT return integer {
Harald Welteb3414b22017-11-23 18:22:10 +0100335 var integer conn_id;
336
337 do {
338 conn_id := float2int(rnd()*SCCP_Emulation.tsp_max_ConnectionId);
339 } while (f_conn_id_known(conn_id) == true);
340
341 return conn_id;
342}
343
Harald Welte365f4ed2017-11-23 00:00:43 +0100344private function f_conn_table_init()
Harald Welte6811d102019-04-14 22:23:14 +0200345runs on RAN_Emulation_CT {
Harald Welte365f4ed2017-11-23 00:00:43 +0100346 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
347 ConnectionTable[i].comp_ref := null;
348 ConnectionTable[i].sccp_conn_id := -1;
Harald Welte2fce7882019-04-15 11:48:05 +0200349#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +0100350 ConnectionTable[i].mgcp_trans_id := omit;
Harald Welte2fce7882019-04-15 11:48:05 +0200351#endif
Harald Weltec82eef42017-11-24 20:40:12 +0100352 ConnectionTable[i].cic := omit;
Harald Welte9dadc522018-02-06 13:56:04 +0100353 ConnectionTable[i].n_sd := { 0, 0, 0, 0 };
Harald Welte365f4ed2017-11-23 00:00:43 +0100354 }
Harald Welte17d21152018-01-27 00:47:11 +0100355 for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) {
356 ImsiTable[i].comp_ref := null;
357 ImsiTable[i].imsi := omit;
358 ImsiTable[i].tmsi := 'FFFFFFFF'O;
359 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100360}
361
Harald Welte6811d102019-04-14 22:23:14 +0200362private function f_conn_table_add(RAN_ConnHdlr comp_ref, integer sccp_conn_id)
363runs on RAN_Emulation_CT {
Harald Welte365f4ed2017-11-23 00:00:43 +0100364 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
365 if (ConnectionTable[i].sccp_conn_id == -1) {
366 ConnectionTable[i].comp_ref := comp_ref;
367 ConnectionTable[i].sccp_conn_id := sccp_conn_id;
Harald Welte9dadc522018-02-06 13:56:04 +0100368 ConnectionTable[i].n_sd := { 0, 0, 0, 0 };
Harald Welteb3414b22017-11-23 18:22:10 +0100369 log("Added conn table entry ", i, comp_ref, sccp_conn_id);
Harald Welte365f4ed2017-11-23 00:00:43 +0100370 return;
371 }
372 }
Harald Welte6811d102019-04-14 22:23:14 +0200373 testcase.stop("RAN Connection table full!");
Harald Welte365f4ed2017-11-23 00:00:43 +0100374}
375
376private function f_conn_table_del(integer sccp_conn_id)
Harald Welte6811d102019-04-14 22:23:14 +0200377runs on RAN_Emulation_CT {
Harald Welte365f4ed2017-11-23 00:00:43 +0100378 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
379 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
Harald Welteb3414b22017-11-23 18:22:10 +0100380 log("Deleted conn table entry ", i,
381 ConnectionTable[i].comp_ref, sccp_conn_id);
Harald Welte365f4ed2017-11-23 00:00:43 +0100382 ConnectionTable[i].sccp_conn_id := -1;
383 ConnectionTable[i].comp_ref := null;
Harald Welte0a4317a2017-11-25 00:32:46 +0100384 return
Harald Welte365f4ed2017-11-23 00:00:43 +0100385 }
386 }
Harald Welte6811d102019-04-14 22:23:14 +0200387 setverdict(fail, "RAN Connection table attempt to delete non-existant ", sccp_conn_id);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200388 mtc.stop;
Harald Welte365f4ed2017-11-23 00:00:43 +0100389}
390
Harald Welte17d21152018-01-27 00:47:11 +0100391private function f_imsi_table_find(hexstring imsi, template OCT4 tmsi)
Harald Welte6811d102019-04-14 22:23:14 +0200392runs on RAN_Emulation_CT return RAN_ConnHdlr {
Harald Welte17d21152018-01-27 00:47:11 +0100393 for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) {
394 if (ImsiTable[i].imsi == imsi or
395 isvalue(tmsi) and match(ImsiTable[i].tmsi, tmsi)) {
396 return ImsiTable[i].comp_ref;
397 }
398 }
399 return null;
400}
401
Harald Welte2fce7882019-04-15 11:48:05 +0200402#ifdef RAN_EMULATION_BSSAP
403type record BSSAP_Conn_Req {
404 SCCP_PAR_Address addr_peer,
405 SCCP_PAR_Address addr_own,
406 PDU_BSSAP bssap
407}
408template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := {
409 addr_peer := peer,
410 addr_own := own,
411 bssap := bssap
412};
413
Harald Welte365f4ed2017-11-23 00:00:43 +0100414/* handle (optional) userData portion of various primitives and dispatch it to the client */
Harald Welte6811d102019-04-14 22:23:14 +0200415private function f_handle_userData(RAN_ConnHdlr client, PDU_BSSAP bssap)
416runs on RAN_Emulation_CT {
Harald Welte365f4ed2017-11-23 00:00:43 +0100417 /* decode + send decoded BSSAP to client */
Harald Welte1b2748e2017-11-24 23:40:16 +0100418
Harald Welte473676b2018-01-27 20:38:01 +0100419 if (ischosen(bssap.pdu.bssmap)) {
420 /* BSC Side: If this is an assignment command, store CIC */
421 if (ischosen(bssap.pdu.bssmap.assignmentRequest) and
422 ispresent(bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode)) {
423 var BSSMAP_IE_CircuitIdentityCode cic_ie :=
424 bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode;
Pau Espin Pedrol43021cb2019-06-18 17:32:15 +0200425 var integer cic := f_bssmap_ie_cic_2_int(cic_ie);
Harald Welte473676b2018-01-27 20:38:01 +0100426 f_comp_store_cic(client, cic);
427 }
Harald Welte1b2748e2017-11-24 23:40:16 +0100428 }
429
Harald Welte6811d102019-04-14 22:23:14 +0200430 if (ischosen(bssap.pdu.dtap) and g_ran_ops.decode_dtap) {
431 if (g_ran_ops.role_ms) {
Harald Welte0b476062018-01-21 19:07:32 +0100432 /* we are the MS, so any message to us must be MT */
433 var PDU_DTAP_MT mt := {
434 dlci := bssap.dlci,
435 dtap := dec_PDU_ML3_NW_MS(bssap.pdu.dtap)
436 };
437 CLIENT.send(mt) to client;
438 } else {
439 /* we are the Network, so any message to us must be MO */
440 var PDU_DTAP_MO mo := {
441 dlci := bssap.dlci,
442 dtap := dec_PDU_ML3_MS_NW(bssap.pdu.dtap)
443 };
444 CLIENT.send(mo) to client;
445 }
446 } else {
447 CLIENT.send(bssap) to client;
448 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100449}
450
451/* call-back type, to be provided by specific implementation; called when new SCCP connection
452 * arrives */
Harald Welte004f5fb2017-12-16 17:54:40 +0100453type function BssmapCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id)
Harald Welte6811d102019-04-14 22:23:14 +0200454runs on RAN_Emulation_CT return RAN_ConnHdlr;
Harald Welte365f4ed2017-11-23 00:00:43 +0100455
456type function BssmapUnitdataCallback(PDU_BSSAP bssap)
Harald Welte6811d102019-04-14 22:23:14 +0200457runs on RAN_Emulation_CT return template PDU_BSSAP;
Harald Welte365f4ed2017-11-23 00:00:43 +0100458
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200459private function append_osmux_ie()
460runs on RAN_Emulation_CT return boolean {
461 return g_ran_ops.use_osmux and (g_ran_ops.transport == BSSAP_TRANSPORT_AoIP);
462}
463
Harald Welte17d21152018-01-27 00:47:11 +0100464/* handle common Unitdata such as Paging */
465private function CommonBssmapUnitdataCallback(PDU_BSSAP bssap)
Harald Welte6811d102019-04-14 22:23:14 +0200466runs on RAN_Emulation_CT return template PDU_BSSAP {
Harald Welte17d21152018-01-27 00:47:11 +0100467 if (match(bssap, tr_BSSMAP_Paging)) {
Harald Welte6811d102019-04-14 22:23:14 +0200468 var RAN_ConnHdlr client := null;
Harald Welte17d21152018-01-27 00:47:11 +0100469 client := f_imsi_table_find(bssap.pdu.bssmap.paging.iMSI.digits,
470 bssap.pdu.bssmap.paging.tMSI.tmsiOctets);
Daniel Willmannabc73e12019-04-15 17:25:56 +0200471 if (client != null) {
Harald Welte17d21152018-01-27 00:47:11 +0100472 log("CommonBssmapUnitdataCallback: IMSI/TMSI found in table, dispatching to ",
473 client);
474 CLIENT.send(bssap) to client;
475 return omit;
476 }
477 log("CommonBssmapUnitdataCallback: IMSI/TMSI not found in table");
478 } else {
479 log("CommonBssmapUnitdataCallback: Not a paging message");
480 }
481 /* ELSE: handle in user callback */
Harald Welte6811d102019-04-14 22:23:14 +0200482 return g_ran_ops.unitdata_cb.apply(bssap);
Harald Welte17d21152018-01-27 00:47:11 +0100483}
484
Harald Welte6811d102019-04-14 22:23:14 +0200485private function f_bssap_wait_for_reset() runs on RAN_Emulation_CT {
Harald Weltea4ca4462018-02-09 00:17:14 +0100486 var BSSAP_N_UNITDATA_ind ud_ind;
487 timer T := 20.0;
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200488 var boolean append_osmux_support := append_osmux_ie();
Harald Weltea4ca4462018-02-09 00:17:14 +0100489
490 T.start;
491 alt {
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200492 [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(append_osmux_support))) -> value ud_ind {
Harald Weltea4ca4462018-02-09 00:17:14 +0100493 BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress,
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200494 ts_BSSMAP_ResetAck(append_osmux_support)));
Harald Weltea4ca4462018-02-09 00:17:14 +0100495 }
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200496 [] as_reset_ack(append_osmux_support);
Harald Weltea4ca4462018-02-09 00:17:14 +0100497 [] BSSAP.receive {
498 repeat;
499 }
500 [] T.timeout {
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200501 setverdict(fail, "Timeout waiting for BSSAP RESET");
502 mtc.stop;
Harald Weltea4ca4462018-02-09 00:17:14 +0100503 }
504 }
505}
506
Harald Welte6811d102019-04-14 22:23:14 +0200507function f_bssap_reset(SCCP_PAR_Address peer, SCCP_PAR_Address own) runs on RAN_Emulation_CT {
Harald Weltea4ca4462018-02-09 00:17:14 +0100508 timer T := 5.0;
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200509 var boolean append_osmux_support := append_osmux_ie();
Harald Weltea4ca4462018-02-09 00:17:14 +0100510
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200511 BSSAP.send(ts_BSSAP_UNITDATA_req(peer, own, ts_BSSMAP_Reset(0, append_osmux_support)));
Harald Weltea4ca4462018-02-09 00:17:14 +0100512 T.start;
513 alt {
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200514 [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(own, peer, tr_BSSMAP_ResetAck(append_osmux_support))) {
Harald Weltea4ca4462018-02-09 00:17:14 +0100515 log("Received RESET-ACK in response to RESET, we're ready to go!");
516 }
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200517 [] as_reset_ack(append_osmux_support);
Harald Weltea4ca4462018-02-09 00:17:14 +0100518 [] BSSAP.receive { repeat };
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200519 [] T.timeout {
520 setverdict(fail, "Timeout waiting for RESET-ACK after sending RESET");
521 mtc.stop;
522 }
Harald Weltea4ca4462018-02-09 00:17:14 +0100523 }
524}
525
Harald Welte2fce7882019-04-15 11:48:05 +0200526private function f_bssap_l3_is_rr(PDU_BSSAP bssap) return boolean {
527 var template octetstring l3 := f_bssap_extract_l3(bssap);
528 return f_L3_is_rr(l3);
529}
530#endif
Harald Welte365f4ed2017-11-23 00:00:43 +0100531
Harald Welte5b027622019-04-14 23:40:17 +0200532#ifdef RAN_EMULATION_RANAP
533type record RANAP_Conn_Req {
534 SCCP_PAR_Address addr_peer,
535 SCCP_PAR_Address addr_own,
536 RANAP_PDU ranap
537}
538template (value) RANAP_Conn_Req ts_RANAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, RANAP_PDU ranap) := {
539 addr_peer := peer,
540 addr_own := own,
541 ranap := ranap
542};
543
544private function fake_dlci_from_sapi(template (omit) SAPI sapi) return template (omit) OCT1
545{
546 if (istemplatekind(sapi, "omit")) {
547 return omit;
548 } else if (valueof(sapi) == sapi_3) {
549 return '03'O;
550 }
551 return '00'O;
552}
553
554private function f_handle_userData_RANAP(RAN_ConnHdlr client, RANAP_PDU ranap)
555runs on RAN_Emulation_CT {
556 /* decode + send decoded RANAP to client */
557 var template (omit) octetstring l3 := f_ranap_extract_l3(ranap);
558 if (istemplatekind(l3, "omit")) {
559 CLIENT.send(ranap) to client;
560 } else {
561 var template (omit) SAPI sapi := f_ranap_extract_sapi(ranap);
562 var template (omit) OCT1 dlci := fake_dlci_from_sapi(sapi);
563 if (g_ran_ops.role_ms) {
564 /* we are the MS, so any message to us must be MT */
565 var PDU_DTAP_MT mt := {
566 dlci := omit,
567 dtap := dec_PDU_ML3_NW_MS(valueof(l3))
568 };
569 if (isvalue(dlci)) {
570 mt.dlci := valueof(dlci)
571 }
572 CLIENT.send(mt) to client;
573 } else {
574 /* we are the Network, so any message to us must be MO */
575 var PDU_DTAP_MO mo := {
576 dlci := omit,
577 dtap := dec_PDU_ML3_MS_NW(valueof(l3))
578 };
579 if (isvalue(dlci)) {
580 mo.dlci := valueof(dlci)
581 }
582 CLIENT.send(mo) to client;
583 }
584 }
585}
586
587/* call-back type, to be provided by specific implementation; called when new SCCP connection
588 * arrives */
589type function RanapCreateCallback(RANAP_N_CONNECT_ind conn_ind, charstring id)
590runs on RAN_Emulation_CT return RAN_ConnHdlr;
591
592type function RanapUnitdataCallback(RANAP_PDU ranap)
593runs on RAN_Emulation_CT return template RANAP_PDU;
594
595private function CommonRanapUnitdataCallback(RANAP_PDU ranap)
596runs on RAN_Emulation_CT return template RANAP_PDU {
597 if (match(ranap, tr_RANAP_Paging(?, ?))) {
598 var RAN_ConnHdlr client := null;
599 /* extract IMSI and (if present) TMSI */
600 var IMSI imsi := ranap.initiatingMessage.value_.paging.protocolIEs[1].value_.permanentNAS_UE_ID.iMSI;
601 var template OCT4 tmsi := omit;
602 if (lengthof(ranap.initiatingMessage.value_.paging.protocolIEs) > 2 and
603 ranap.initiatingMessage.value_.paging.protocolIEs[2].id == id_TemporaryUE_ID) {
604 var TemporaryUE_ID ue_id;
605 ue_id := ranap.initiatingMessage.value_.paging.protocolIEs[2].value_.temporaryUE_ID;
606 if (ischosen(ue_id.tMSI)) {
607 tmsi := ue_id.tMSI;
608 } else {
609 tmsi := ue_id.p_TMSI;
610 }
611 }
612 client := f_imsi_table_find(oct2hex(imsi), tmsi);
Harald Welte03d4e2b2019-05-10 00:42:33 +0200613 if (client != null) {
Harald Welte5b027622019-04-14 23:40:17 +0200614 log("CommonRanapUnitdataCallback: IMSI/TMSI found in table, dispatching to ",
615 client);
616 CLIENT.send(ranap) to client;
617 return omit;
618 }
619 log("CommonRanapUnitdataCallback: IMSI/TMSI not found in table");
620 } else {
621 log("CommonRanapUnitdataCallback: Not a paging message");
622 }
623
624 /* ELSE: handle in user callback */
625 return g_ran_ops.ranap_unitdata_cb.apply(ranap);
626}
627
628private function f_ranap_l3_is_rr(RANAP_PDU ranap) return boolean {
629 var template (omit) SAPI sapi;
630 var template octetstring l3 := f_ranap_extract_l3(ranap);
631 return f_L3_is_rr(l3);
632}
633
634function f_ranap_reset(SCCP_PAR_Address peer, SCCP_PAR_Address own) runs on RAN_Emulation_CT {
635 timer T := 5.0;
636 var CN_DomainIndicator dom;
637 if (g_ran_ops.ps_domain) {
638 dom := ps_domain;
639 } else {
640 dom := cs_domain;
641 }
642
643 RANAP.send(ts_RANAP_UNITDATA_req(peer, own, ts_RANAP_Reset(ts_RanapCause_om_intervention, dom)));
644 T.start;
645 alt {
646 [] RANAP.receive(tr_RANAP_UNITDATA_ind(own, peer, tr_RANAP_ResetAck)) {
647 log("Received RESET-ACK in response to RESET, we're ready to go!");
648 }
649 [] as_reset_ack();
650 [] RANAP.receive { repeat };
651 [] T.timeout {
652 setverdict(fail, "Timeout waiting for RESET-ACK after sending RESET");
653 mtc.stop;
654 }
655 }
656}
657#endif
Harald Welte365f4ed2017-11-23 00:00:43 +0100658
Harald Welte2fce7882019-04-15 11:48:05 +0200659
660type enumerated RanProtocol {
Harald Welte5b027622019-04-14 23:40:17 +0200661 RAN_PROTOCOL_BSSAP,
662 RAN_PROTOCOL_RANAP
Harald Welte2fce7882019-04-15 11:48:05 +0200663}
664
665type record RanOps {
666#ifdef RAN_EMULATION_BSSAP
667 BssmapCreateCallback create_cb optional,
668 BssmapUnitdataCallback unitdata_cb optional,
669#endif
Harald Welte5b027622019-04-14 23:40:17 +0200670#ifdef RAN_EMULATION_RANAP
671 RanapCreateCallback ranap_create_cb optional,
672 RanapUnitdataCallback ranap_unitdata_cb optional,
673 boolean ps_domain,
674#endif
Harald Welte2fce7882019-04-15 11:48:05 +0200675 boolean decode_dtap,
676 boolean role_ms,
677 RanProtocol protocol,
Pau Espin Pedrolc6b78ff2019-06-06 15:58:17 +0200678 RAN_Transport transport,
Pau Espin Pedrolc6a53db2019-05-20 19:31:47 +0200679 boolean use_osmux,
Harald Welte2fce7882019-04-15 11:48:05 +0200680 /* needed for performing BSSMAP RESET */
681 SCCP_PAR_Address sccp_addr_local optional,
682 SCCP_PAR_Address sccp_addr_peer optional
683}
684
685template BIT4 t_ML3_DISC_CC_MM_SS := ('0011'B, '0101'B, '1011'B);
686
687private function f_L3_is_rr(template octetstring l3) return boolean {
688 if (not isvalue(l3)) {
689 return false;
Harald Weltea4ca4462018-02-09 00:17:14 +0100690 }
Harald Welte2fce7882019-04-15 11:48:05 +0200691 var octetstring l3v := valueof(l3);
692 if (lengthof(l3v) < 1) {
693 return false;
694 }
695 /* lower 4 bits of first octet are protocol discriminator */
696 if ((oct2bit(l3v[0]) and4b '00001111'B) == '00000110'B) {
697 return true;
698 }
699 return false;
700}
Harald Weltea4ca4462018-02-09 00:17:14 +0100701
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200702function f_next_n_sd(inout N_Sd_Array n_sd, in integer n_sd_idx) return uint2_t {
Harald Welte2fce7882019-04-15 11:48:05 +0200703 var uint2_t seq_nr;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200704 if (n_sd_idx == 0) {
705 seq_nr := n_sd[0];
706 n_sd[0] := (n_sd[0] + 1) mod 4;
707 } else if (n_sd_idx >= 1 and n_sd_idx <= 3) {
708 seq_nr := n_sd[n_sd_idx];
709 n_sd[n_sd_idx] := (n_sd[n_sd_idx] + 1) mod 2;
Harald Welte2fce7882019-04-15 11:48:05 +0200710 } else {
711 /* no sequence number to patch */
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200712 seq_nr := 0;
Harald Welte2fce7882019-04-15 11:48:05 +0200713 }
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200714 return seq_nr;
715}
716
717/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */
718function f_ML3_patch_seq_nr(in uint2_t seq_nr, inout octetstring enc_l3) {
Harald Welte2fce7882019-04-15 11:48:05 +0200719 log("patching N(SD)=", seq_nr, " into dtap ", enc_l3);
720 enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6);
721 log("patched enc_l3: ", enc_l3);
722}
723
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200724function f_ML3_n_sd_idx(in PDU_ML3_MS_NW dtap) return integer {
725 var uint2_t seq_nr;
726 if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) {
727 return 0;
728 } else if (ischosen(dtap.msgs.gcc)) {
729 return 1;
730 } else if (ischosen(dtap.msgs.bcc)) {
731 return 2;
732 } else if (ischosen(dtap.msgs.loc)) {
733 return 3;
734 }
735 /* no sequence number to patch */
736 return -1;
737}
738
739/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */
740function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) {
741 var integer n_sd_idx := f_ML3_n_sd_idx(dtap);
742 if (n_sd_idx < 0) {
743 return;
744 }
745 var uint2_t seq_nr := f_next_n_sd(cd.n_sd, n_sd_idx);
746 f_ML3_patch_seq_nr(seq_nr, enc_l3);
747}
748
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200749private altstep as_reset_ack(boolean append_osmux_support := false) runs on RAN_Emulation_CT {
Harald Welte2fce7882019-04-15 11:48:05 +0200750#ifdef RAN_EMULATION_BSSAP
751 var BSSAP_N_UNITDATA_ind ud_ind;
752#endif
Harald Welte5b027622019-04-14 23:40:17 +0200753#ifdef RAN_EMULATION_RANAP
754 var RANAP_N_UNITDATA_ind rud_ind;
755#endif
Harald Welte2fce7882019-04-15 11:48:05 +0200756#ifdef RAN_EMULATION_BSSAP
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200757 [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(append_osmux_support))) -> value ud_ind {
Harald Welte2fce7882019-04-15 11:48:05 +0200758 log("Respoding to inbound RESET with RESET-ACK");
759 BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress,
Pau Espin Pedrol95549182019-06-06 16:17:30 +0200760 ts_BSSMAP_ResetAck(append_osmux_support)));
Harald Welte2fce7882019-04-15 11:48:05 +0200761 repeat;
762 }
763#endif
Harald Welte5b027622019-04-14 23:40:17 +0200764#ifdef RAN_EMULATION_RANAP
765 [] RANAP.receive(tr_RANAP_UNITDATA_ind(?, ?, tr_RANAP_Reset)) -> value rud_ind {
766 log("Respoding to inbound IuRESET with IuRESET-ACK");
767 var CN_DomainIndicator dom;
768 dom := rud_ind.userData.initiatingMessage.value_.Reset.protocolIEs[1].value_.cN_DomainIndicator;
769 RANAP.send(ts_RANAP_UNITDATA_req(rud_ind.callingAddress, rud_ind.calledAddress,
770 ts_RANAP_ResetAck(dom)));
771 }
772#endif
Harald Welte2fce7882019-04-15 11:48:05 +0200773}
774
775
776private altstep as_main_bssap() runs on RAN_Emulation_CT {
777#ifdef RAN_EMULATION_BSSAP
Harald Welte004f5fb2017-12-16 17:54:40 +0100778 var BSSAP_N_UNITDATA_ind ud_ind;
779 var BSSAP_N_CONNECT_ind conn_ind;
780 var BSSAP_N_CONNECT_cfm conn_cfm;
781 var BSSAP_N_DATA_ind data_ind;
782 var BSSAP_N_DISCONNECT_ind disc_ind;
Harald Welteb3414b22017-11-23 18:22:10 +0100783 var BSSAP_Conn_Req creq;
Harald Welte365f4ed2017-11-23 00:00:43 +0100784 var PDU_BSSAP bssap;
Harald Welte2fce7882019-04-15 11:48:05 +0200785 var RAN_ConnHdlr vc_conn;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200786 var integer targetPointCode;
787 var N_Sd_Array last_n_sd;
Harald Welte365f4ed2017-11-23 00:00:43 +0100788
Harald Welte365f4ed2017-11-23 00:00:43 +0100789 /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
Harald Welte004f5fb2017-12-16 17:54:40 +0100790 [] BSSAP.receive(BSSAP_N_UNITDATA_ind:?) -> value ud_ind {
Harald Welte365f4ed2017-11-23 00:00:43 +0100791 /* Connectionless Procedures like RESET */
792 var template PDU_BSSAP resp;
Harald Welte17d21152018-01-27 00:47:11 +0100793 resp := CommonBssmapUnitdataCallback(ud_ind.userData);
Harald Welte365f4ed2017-11-23 00:00:43 +0100794 if (isvalue(resp)) {
Harald Welte004f5fb2017-12-16 17:54:40 +0100795 BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress,
796 ud_ind.calledAddress, resp));
Harald Welte365f4ed2017-11-23 00:00:43 +0100797 }
798 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100799 /* SCCP -> Client: new connection from BSC */
Harald Welte004f5fb2017-12-16 17:54:40 +0100800 [] BSSAP.receive(BSSAP_N_CONNECT_ind:?) -> value conn_ind {
Harald Welte2fce7882019-04-15 11:48:05 +0200801 vc_conn := g_ran_ops.create_cb.apply(conn_ind, g_ran_id);
Harald Welte365f4ed2017-11-23 00:00:43 +0100802 /* store mapping between client components and SCCP connectionId */
803 f_conn_table_add(vc_conn, conn_ind.connectionId);
804 /* handle user payload */
805 f_handle_userData(vc_conn, conn_ind.userData);
806 /* confirm connection establishment */
Harald Welte004f5fb2017-12-16 17:54:40 +0100807 BSSAP.send(ts_BSSAP_CONNECT_res(conn_ind.connectionId, omit));
Harald Welte365f4ed2017-11-23 00:00:43 +0100808 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100809 /* SCCP -> Client: connection-oriented data in existing connection */
Harald Welte004f5fb2017-12-16 17:54:40 +0100810 [] BSSAP.receive(BSSAP_N_DATA_ind:?) -> value data_ind {
Harald Welte365f4ed2017-11-23 00:00:43 +0100811 vc_conn := f_comp_by_conn_id(data_ind.connectionId);
Harald Welte5cc4aa22017-11-23 18:51:28 +0100812 if (ispresent(data_ind.userData)) {
813 f_handle_userData(vc_conn, data_ind.userData);
814 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100815 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100816 /* SCCP -> Client: disconnect of an existing connection */
Harald Welte004f5fb2017-12-16 17:54:40 +0100817 [] BSSAP.receive(BSSAP_N_DISCONNECT_ind:?) -> value disc_ind {
Harald Welte365f4ed2017-11-23 00:00:43 +0100818 vc_conn := f_comp_by_conn_id(disc_ind.connectionId);
Harald Welte5cc4aa22017-11-23 18:51:28 +0100819 if (ispresent(disc_ind.userData)) {
820 f_handle_userData(vc_conn, disc_ind.userData);
821 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100822 /* notify client about termination */
Harald Welte6811d102019-04-14 22:23:14 +0200823 var RAN_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND;
Harald Welte365f4ed2017-11-23 00:00:43 +0100824 CLIENT.send(prim) to vc_conn;
825 f_conn_table_del(disc_ind.connectionId);
826 /* TOOD: return confirm to other side? */
827 }
Harald Welteb3414b22017-11-23 18:22:10 +0100828 /* SCCP -> Client: connection confirm for outbound connection */
Harald Welte004f5fb2017-12-16 17:54:40 +0100829 [] BSSAP.receive(BSSAP_N_CONNECT_cfm:?) -> value conn_cfm {
Harald Welte71b69332018-01-21 20:43:53 +0100830 vc_conn := f_comp_by_conn_id(conn_cfm.connectionId);
Harald Welte6811d102019-04-14 22:23:14 +0200831 var RAN_Conn_Prim prim := MSC_CONN_PRIM_CONF_IND;
Harald Welte71b69332018-01-21 20:43:53 +0100832 CLIENT.send(prim) to vc_conn;
Harald Welteb3414b22017-11-23 18:22:10 +0100833 /* handle user payload */
Harald Welte5cc4aa22017-11-23 18:51:28 +0100834 if (ispresent(conn_cfm.userData)) {
835 f_handle_userData(vc_conn, conn_cfm.userData);
836 }
Harald Welteb3414b22017-11-23 18:22:10 +0100837 }
Harald Welte2fce7882019-04-15 11:48:05 +0200838 [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn {
839 var integer conn_id := f_conn_id_by_comp(vc_conn);
840 /* send it to dispatcher */
841 BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap));
842 }
Harald Welteb3414b22017-11-23 18:22:10 +0100843
Harald Welte365f4ed2017-11-23 00:00:43 +0100844 /* Disconnect request client -> SCCP */
Harald Welte6811d102019-04-14 22:23:14 +0200845 [] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
Harald Welte365f4ed2017-11-23 00:00:43 +0100846 var integer conn_id := f_conn_id_by_comp(vc_conn);
Harald Welte004f5fb2017-12-16 17:54:40 +0100847 BSSAP.send(ts_BSSAP_DISC_req(conn_id, 0));
Harald Welte365f4ed2017-11-23 00:00:43 +0100848 f_conn_table_del(conn_id);
849 }
850
851 /* BSSAP from client -> SCCP */
Harald Welteb3414b22017-11-23 18:22:10 +0100852 [] CLIENT.receive(BSSAP_Conn_Req:?) -> value creq sender vc_conn {
853 var integer conn_id;
Harald Welte004f5fb2017-12-16 17:54:40 +0100854 /* send to dispatcher */
Harald Welteb3414b22017-11-23 18:22:10 +0100855
856 if (f_comp_known(vc_conn) == false) {
857 /* unknown client, create new connection */
858 conn_id := f_gen_conn_id();
859
860 /* store mapping between client components and SCCP connectionId */
861 f_conn_table_add(vc_conn, conn_id);
862
Harald Welte004f5fb2017-12-16 17:54:40 +0100863 BSSAP.send(ts_BSSAP_CONNECT_req(creq.addr_peer, creq.addr_own, conn_id,
864 creq.bssap));
Harald Welteb3414b22017-11-23 18:22:10 +0100865 } else {
866 /* known client, send via existing connection */
867 conn_id := f_conn_id_by_comp(vc_conn);
Harald Welte004f5fb2017-12-16 17:54:40 +0100868 BSSAP.send(ts_BSSAP_DATA_req(conn_id, creq.bssap));
Harald Welteb3414b22017-11-23 18:22:10 +0100869 }
870
Harald Welte49518bf2018-02-10 11:39:19 +0100871 /* InitialL3 contains RR (PAG RESP) or MM (CM SRV REQ), we must increment
872 * counter only on MM/CC/SS, but not on RR */
Harald Welte6811d102019-04-14 22:23:14 +0200873 if (g_ran_ops.role_ms and not f_bssap_l3_is_rr(creq.bssap)) {
Harald Welte9dadc522018-02-06 13:56:04 +0100874 /* we have just sent the first MM message, increment the counter */
875 var integer idx := f_idx_by_comp(vc_conn);
876 ConnectionTable[idx].n_sd[0] := 1;
877 log("patch: N(SD) for ConnIdx ", idx, " set to 1");
878 }
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200879 }
Harald Welte9dadc522018-02-06 13:56:04 +0100880
Neels Hofmeyr0ac63152019-05-07 01:20:17 +0200881 [] PROC.getcall(RAN_last_n_sd:{?,-}) -> param(vc_conn) {
882 var integer idx := f_idx_by_comp(vc_conn);
883 last_n_sd := ConnectionTable[idx].n_sd;
884 PROC.reply(RAN_last_n_sd:{vc_conn, last_n_sd}) to vc_conn;
885 }
886
887 [] PROC.getcall(RAN_continue_after_n_sd:{?,?}) -> param(last_n_sd, vc_conn) {
888 var integer idx := f_idx_by_comp(vc_conn);
889 ConnectionTable[idx].n_sd := last_n_sd;
890 PROC.reply(RAN_continue_after_n_sd:{last_n_sd, vc_conn}) to vc_conn;
Harald Welteb3414b22017-11-23 18:22:10 +0100891 }
Harald Welte2fce7882019-04-15 11:48:05 +0200892#else
Harald Welte5b027622019-04-14 23:40:17 +0200893 [false] CLIENT.receive {}
894#endif
895}
896
897private altstep as_main_ranap() runs on RAN_Emulation_CT {
898#ifdef RAN_EMULATION_RANAP
899 var RANAP_N_UNITDATA_ind rud_ind;
900 var RANAP_N_CONNECT_ind rconn_ind;
901 var RANAP_N_CONNECT_cfm rconn_cfm;
902 var RANAP_N_DATA_ind rdata_ind;
903 var RANAP_N_DISCONNECT_ind rdisc_ind;
904 var RANAP_Conn_Req creq;
905 var RANAP_PDU ranap;
906 var RAN_ConnHdlr vc_conn;
907
908 /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
909 [] RANAP.receive(RANAP_N_UNITDATA_ind:?) -> value rud_ind {
910 /* Connectionless Procedures like RESET */
911 var template RANAP_PDU resp;
912 resp := CommonRanapUnitdataCallback(rud_ind.userData);
913 if (isvalue(resp)) {
914 RANAP.send(ts_RANAP_UNITDATA_req(rud_ind.callingAddress,
915 rud_ind.calledAddress, resp));
916 }
917 }
918 /* SCCP -> Client: new connection from BSC */
919 [] RANAP.receive(RANAP_N_CONNECT_ind:?) -> value rconn_ind {
920 vc_conn := g_ran_ops.ranap_create_cb.apply(rconn_ind, g_ran_id);
921 /* store mapping between client components and SCCP connectionId */
922 f_conn_table_add(vc_conn, rconn_ind.connectionId);
923 /* handle user payload */
924 f_handle_userData_RANAP(vc_conn, rconn_ind.userData);
925 /* confirm connection establishment */
926 RANAP.send(ts_RANAP_CONNECT_res(rconn_ind.connectionId, omit));
927 }
928 /* SCCP -> Client: connection-oriented data in existing connection */
929 [] RANAP.receive(RANAP_N_DATA_ind:?) -> value rdata_ind {
930 vc_conn := f_comp_by_conn_id(rdata_ind.connectionId);
931 if (ispresent(rdata_ind.userData)) {
932 f_handle_userData_RANAP(vc_conn, rdata_ind.userData);
933 }
934 }
935 /* SCCP -> Client: disconnect of an existing connection */
936 [] RANAP.receive(RANAP_N_DISCONNECT_ind:?) -> value rdisc_ind {
937 vc_conn := f_comp_by_conn_id(rdisc_ind.connectionId);
938 if (ispresent(rdisc_ind.userData)) {
939 f_handle_userData_RANAP(vc_conn, rdisc_ind.userData);
940 }
941 /* notify client about termination */
942 var RAN_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND;
943 CLIENT.send(prim) to vc_conn;
944 f_conn_table_del(rdisc_ind.connectionId);
945 /* TOOD: return confirm to other side? */
946 }
947 /* SCCP -> Client: connection confirm for outbound connection */
948 [] RANAP.receive(RANAP_N_CONNECT_cfm:?) -> value rconn_cfm {
949 vc_conn := f_comp_by_conn_id(rconn_cfm.connectionId);
950 var RAN_Conn_Prim prim := MSC_CONN_PRIM_CONF_IND;
951 CLIENT.send(prim) to vc_conn;
952 /* handle user payload */
953 if (ispresent(rconn_cfm.userData)) {
954 f_handle_userData_RANAP(vc_conn, rconn_cfm.userData);
955 }
956 }
957
958 [] CLIENT.receive(RANAP_PDU:?) -> value ranap sender vc_conn {
959 var integer conn_id := f_conn_id_by_comp(vc_conn);
960 /* send it to dispatcher */
961 RANAP.send(ts_RANAP_DATA_req(conn_id, ranap));
962 }
963
964 /* Disconnect request client -> SCCP */
965 [] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
966 var integer conn_id := f_conn_id_by_comp(vc_conn);
967 RANAP.send(ts_RANAP_DISC_req(conn_id, 0));
968 f_conn_table_del(conn_id);
969 }
970
971 /* BSSAP from client -> SCCP */
972 [] CLIENT.receive(RANAP_Conn_Req:?) -> value creq sender vc_conn {
973 var integer conn_id;
974 /* send to dispatcher */
975
976 if (f_comp_known(vc_conn) == false) {
977 /* unknown client, create new connection */
978 conn_id := f_gen_conn_id();
979
980 /* store mapping between client components and SCCP connectionId */
981 f_conn_table_add(vc_conn, conn_id);
982
983 RANAP.send(ts_RANAP_CONNECT_req(creq.addr_peer, creq.addr_own, conn_id,
984 creq.ranap));
985 } else {
986 /* known client, send via existing connection */
987 conn_id := f_conn_id_by_comp(vc_conn);
988 RANAP.send(ts_RANAP_DATA_req(conn_id, creq.ranap));
989 }
990
991 /* InitialL3 contains RR (PAG RESP) or MM (CM SRV REQ), we must increment
992 * counter only on MM/CC/SS, but not on RR */
993 if (g_ran_ops.role_ms and not f_ranap_l3_is_rr(creq.ranap)) {
994 /* we have just sent the first MM message, increment the counter */
995 var integer idx := f_idx_by_comp(vc_conn);
996 ConnectionTable[idx].n_sd[0] := 1;
997 log("patch: N(SD) for ConnIdx ", idx, " set to 1");
998 }
999 }
1000
1001#else
1002 [false] CLIENT.receive {}
Harald Welte2fce7882019-04-15 11:48:05 +02001003#endif
1004}
Harald Welteb3414b22017-11-23 18:22:10 +01001005
Harald Welte2fce7882019-04-15 11:48:05 +02001006private altstep as_main_mgcp() runs on RAN_Emulation_CT {
1007#ifdef RAN_EMULATION_MGCP
1008 var MgcpCommand mgcp_req;
1009 var MgcpResponse mgcp_resp;
1010 var RAN_ConnHdlr vc_conn;
Harald Welte0b476062018-01-21 19:07:32 +01001011
Harald Weltec82eef42017-11-24 20:40:12 +01001012 /* Handling of MGCP in IPA SCCPLite case. This predates 3GPP AoIP
1013 * and uses a MGCP session in parallel to BSSAP. BSSAP uses CIC
1014 * as usual, and MGCP uses "CIC@mgw" endpoint naming, where CIC
1015 * is printed as hex string, e.g. a@mgw for CIC 10 */
1016
1017 /* CLIENT -> MGCP */
1018 [] CLIENT.receive(MgcpCommand:?) -> value mgcp_req sender vc_conn {
1019 /* MGCP request from Handler (we're MSC) */
1020 /* store the transaction ID we've seen */
1021 f_comp_store_mgcp_tid(vc_conn, mgcp_req.line.trans_id);
1022 /* simply forward any MGCP from the client to the port */
1023 MGCP.send(mgcp_req);
1024 }
1025 [] CLIENT.receive(MgcpResponse:?) -> value mgcp_resp sender vc_conn {
1026 /* MGCP response from Handler (we're BSC/MGW) */
1027 /* simply forward any MGCP from the client to the port */
1028 MGCP.send(mgcp_resp);
1029 }
1030
1031 /* MGCP -> CLIENT */
1032 [] MGCP.receive(MgcpCommand:?) -> value mgcp_req {
1033 /* MGCP request from network side (we're BSC/MGW) */
1034 /* Extract CIC from local part of endpoint name */
1035 var integer cic := f_mgcp_ep_extract_cic(mgcp_req.line.ep);
Harald Weltee98bb2e2017-11-29 12:09:48 +01001036 if (match(mgcp_req, tr_RSIP) and f_cic_known(cic) == false) {
1037 /* ignore RSIP for unknown CIC */
1038 } else {
1039 /* Resolve the vc_conn by the CIC */
1040 vc_conn := f_comp_by_cic(cic);
1041 CLIENT.send(mgcp_req) to vc_conn;
1042 }
Harald Weltec82eef42017-11-24 20:40:12 +01001043 }
1044 [] MGCP.receive(MgcpResponse:?) -> value mgcp_resp {
1045 /* MGCP response from network side (we're MSC) */
1046 /* Resolve the vc_conn by the transaction ID */
1047 vc_conn := f_comp_by_mgcp_tid(mgcp_resp.line.trans_id);
1048 CLIENT.send(mgcp_resp) to vc_conn;
1049 }
Harald Welte2fce7882019-04-15 11:48:05 +02001050#else
1051 [false] CLIENT.receive {}
1052#endif
1053}
Harald Weltec82eef42017-11-24 20:40:12 +01001054
Pau Espin Pedrol4d0e5e52019-06-07 19:38:28 +02001055private altstep as_main_ctrl() runs on RAN_Emulation_CT {
1056#ifdef RAN_EMULATION_CTRL
1057 var CtrlMessage ctrl;
1058 var RAN_ConnHdlr vc_conn;
1059 var ASP_IPA_Event evt;
1060
1061 /* Handling of CTRL in IPA SCCPLite case. This predates 3GPP AoIP
1062 * and uses a CTRL session in parallel to BSSAP. */
1063
1064 /* CTRL_CLIENT -> CTRL */
1065 [] CTRL_CLIENT.receive(CtrlMessage:?) -> value ctrl sender vc_conn {
1066 CTRL.send(ctrl);
1067 }
1068
1069 /* CTRL -> CTRL_CLIENT */
1070 [] CTRL.receive(CtrlMessage:?) -> value ctrl {
1071 CTRL_CLIENT.send(ctrl);
1072 }
1073
1074 [] CTRL.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) -> value evt {
1075 CTRL_CLIENT.send(evt);
1076 }
1077 [] CTRL.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_DOWN}) {
1078 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Lost IPA connection!");
1079 }
1080 [] CTRL.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) {}
1081#else
1082 [false] CLIENT.receive {}
1083#endif
1084}
1085
Harald Welte2fce7882019-04-15 11:48:05 +02001086/* send a raw (encoded) L3 message over given SCCP connection */
1087private function f_xmit_raw_l3(integer sccp_conn_id, OCT1 dlci, octetstring l3_enc) runs on RAN_Emulation_CT
1088{
1089 select (g_ran_ops.protocol) {
1090#ifdef RAN_EMULATION_BSSAP
1091 case (RAN_PROTOCOL_BSSAP) {
1092 var PDU_BSSAP bssap;
1093 bssap := valueof(ts_BSSAP_DTAP(l3_enc, dlci));
1094 BSSAP.send(ts_BSSAP_DATA_req(sccp_conn_id, bssap));
1095 }
1096#endif
Harald Welte5b027622019-04-14 23:40:17 +02001097#ifdef RAN_EMULATION_RANAP
1098 case (RAN_PROTOCOL_RANAP) {
1099 var RANAP_PDU ranap;
1100 if (false /* SAPI */) {
1101 var RANAP_IEs.SAPI sapi := sapi_0;
1102 ranap := valueof(ts_RANAP_DirectTransferSAPI(l3_enc, sapi));
1103 } else {
1104 ranap := valueof(ts_RANAP_DirectTransfer(l3_enc));
1105 }
1106 RANAP.send(ts_RANAP_DATA_req(sccp_conn_id, ranap));
1107 }
1108#endif
Harald Welte2fce7882019-04-15 11:48:05 +02001109 }
1110}
1111
1112function main(RanOps ops, charstring id) runs on RAN_Emulation_CT {
1113
1114 g_ran_id := id;
1115 g_ran_ops := ops;
1116 f_conn_table_init();
1117 f_expect_table_init();
1118
1119 if (isvalue(ops.sccp_addr_peer) and isvalue(ops.sccp_addr_local)) {
1120 f_sleep(1.0); /* HACK to wait for M3UA/ASP to be ACTIVE */
Harald Welte5b027622019-04-14 23:40:17 +02001121 select (g_ran_ops.protocol) {
1122#ifdef RAN_EMULATION_BSSAP
1123 case (RAN_PROTOCOL_BSSAP) {
1124 f_bssap_reset(ops.sccp_addr_peer, ops.sccp_addr_local);
1125 }
1126#endif
1127#ifdef RAN_EMULATION_RANAP
1128 case (RAN_PROTOCOL_RANAP) {
1129 f_ranap_reset(ops.sccp_addr_peer, ops.sccp_addr_local);
1130 }
1131#endif
1132 }
Harald Welte2fce7882019-04-15 11:48:05 +02001133 }
1134
1135 while (true) {
1136 var RAN_ConnHdlr vc_conn;
1137 var PDU_DTAP_MO dtap_mo;
1138 var PDU_DTAP_MT dtap_mt;
1139 var RAN_ConnHdlr vc_hdlr;
1140 var octetstring l3_info;
1141 var hexstring imsi;
1142 var OCT4 tmsi;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001143 var integer targetPointCode;
Harald Welte2fce7882019-04-15 11:48:05 +02001144
1145 alt {
1146 [g_ran_ops.protocol == RAN_PROTOCOL_BSSAP] as_main_bssap();
Harald Welte5b027622019-04-14 23:40:17 +02001147 [g_ran_ops.protocol == RAN_PROTOCOL_RANAP] as_main_ranap();
Harald Welte2fce7882019-04-15 11:48:05 +02001148
1149 [g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MO:?) -> value dtap_mo sender vc_conn {
1150 var integer idx := f_idx_by_comp(vc_conn);
1151 /* convert from decoded DTAP to encoded DTAP */
1152 var octetstring l3_enc := enc_PDU_ML3_MS_NW(dtap_mo.dtap);
1153 /* patch correct L3 send sequence number N(SD) into l3_enc */
1154 if (dtap_mo.skip_seq_patching == false) {
1155 f_ML3_patch_seq(ConnectionTable[idx], dtap_mo.dtap, l3_enc);
1156 }
1157 f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mo.dlci, l3_enc);
1158 }
1159
1160 [not g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MT:?) -> value dtap_mt sender vc_conn {
1161 var integer idx := f_idx_by_comp(vc_conn);
1162 /* convert from decoded DTAP to encoded DTAP */
1163 var octetstring l3_enc := enc_PDU_ML3_NW_MS(dtap_mt.dtap);
1164 f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mt.dlci, l3_enc);
1165 }
1166
1167 [] as_main_mgcp();
Pau Espin Pedrol4d0e5e52019-06-07 19:38:28 +02001168 [] as_main_ctrl();
Harald Welte624f9632017-12-16 19:26:04 +01001169
Harald Welte6811d102019-04-14 22:23:14 +02001170 [] PROC.getcall(RAN_register:{?,?}) -> param(l3_info, vc_hdlr) {
Harald Welte624f9632017-12-16 19:26:04 +01001171 f_create_expect(l3_info, vc_hdlr);
Harald Welte6811d102019-04-14 22:23:14 +02001172 PROC.reply(RAN_register:{l3_info, vc_hdlr}) to vc_hdlr;
Harald Welte624f9632017-12-16 19:26:04 +01001173 }
1174
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001175 [] PROC.getcall(RAN_register_handoverRequest:{?,?}) -> param(targetPointCode, vc_hdlr) {
1176 f_create_expect(omit, vc_hdlr, targetPointCode);
1177 PROC.reply(RAN_register_handoverRequest:{targetPointCode, vc_hdlr}) to vc_hdlr;
1178 }
1179
Harald Welte6811d102019-04-14 22:23:14 +02001180 [] PROC.getcall(RAN_register_imsi:{?,?,?}) -> param(imsi, tmsi, vc_hdlr) {
Harald Welte17d21152018-01-27 00:47:11 +01001181 f_create_imsi(imsi, tmsi, vc_hdlr);
Harald Welte6811d102019-04-14 22:23:14 +02001182 PROC.reply(RAN_register_imsi:{imsi, tmsi, vc_hdlr}) to vc_hdlr;
Harald Welte17d21152018-01-27 00:47:11 +01001183 }
1184
1185
Harald Welte365f4ed2017-11-23 00:00:43 +01001186 }
1187 }
1188}
1189
Harald Welte2fce7882019-04-15 11:48:05 +02001190#ifdef RAN_EMULATION_MGCP
Harald Weltec82eef42017-11-24 20:40:12 +01001191private function f_mgcp_ep_extract_cic(charstring inp) return integer {
Harald Welte525a9c12017-11-24 23:40:41 +01001192 var charstring local_part := regexp(inp, "(*)@*", 0);
Harald Weltec82eef42017-11-24 20:40:12 +01001193 return hex2int(str2hex(local_part));
1194
1195}
Harald Welte2fce7882019-04-15 11:48:05 +02001196#endif
Harald Welte365f4ed2017-11-23 00:00:43 +01001197
Harald Welte624f9632017-12-16 19:26:04 +01001198/***********************************************************************
1199 * "Expect" Handling (mapping for expected incoming SCCP connections)
1200 ***********************************************************************/
1201
1202/* data about an expected future incoming connection */
1203type record ExpectData {
1204 /* L3 payload based on which we can match it */
1205 octetstring l3_payload optional,
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001206 integer handoverRequestPointCode optional,
Harald Welte624f9632017-12-16 19:26:04 +01001207 /* component reference for this connection */
Harald Welte6811d102019-04-14 22:23:14 +02001208 RAN_ConnHdlr vc_conn
Harald Welte624f9632017-12-16 19:26:04 +01001209}
1210
1211/* procedure based port to register for incoming connections */
Harald Welte6811d102019-04-14 22:23:14 +02001212signature RAN_register(in octetstring l3, in RAN_ConnHdlr hdlr);
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001213signature RAN_register_handoverRequest(in integer targetPointCode, in RAN_ConnHdlr hdlr);
Harald Welte624f9632017-12-16 19:26:04 +01001214
Harald Welte17d21152018-01-27 00:47:11 +01001215/* procedure based port to register for incoming IMSI/TMSI */
Harald Welte6811d102019-04-14 22:23:14 +02001216signature RAN_register_imsi(in hexstring imsi, in OCT4 tmsi, in RAN_ConnHdlr hdlr);
Harald Welte17d21152018-01-27 00:47:11 +01001217
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001218/* If DTAP happens across other channels (e.g. GSUP), provide manual advancing of the n_sd sequence number */
1219signature RAN_last_n_sd(in RAN_ConnHdlr hdlr, out N_Sd_Array last_n_sd);
1220
1221/* Update conn's n_sd sequence nr after the connection was taken over from elsewhere */
1222signature RAN_continue_after_n_sd(N_Sd_Array last_n_sd, in RAN_ConnHdlr hdlr);
1223
Harald Welte6811d102019-04-14 22:23:14 +02001224type port RAN_PROC_PT procedure {
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001225 inout RAN_register, RAN_register_imsi, RAN_register_handoverRequest, RAN_last_n_sd, RAN_continue_after_n_sd;
Harald Welte624f9632017-12-16 19:26:04 +01001226} with { extension "internal" };
1227
Harald Welte5b027622019-04-14 23:40:17 +02001228#ifdef RAN_EMULATION_BSSAP
Harald Welte624f9632017-12-16 19:26:04 +01001229/* CreateCallback that can be used as create_cb and will use the expectation table */
1230function ExpectedCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id)
Harald Welte6811d102019-04-14 22:23:14 +02001231runs on RAN_Emulation_CT return RAN_ConnHdlr {
1232 var RAN_ConnHdlr ret := null;
Harald Welte624f9632017-12-16 19:26:04 +01001233 var octetstring l3_info;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001234 var boolean handoverRequest := false;
1235 var integer handoverRequestPointCode;
Harald Welte624f9632017-12-16 19:26:04 +01001236 var integer i;
1237
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001238 if (ischosen(conn_ind.userData.pdu.bssmap.completeLayer3Information)) {
1239 l3_info := conn_ind.userData.pdu.bssmap.completeLayer3Information.layer3Information.layer3info;
1240 log("ExpectedCreateCallback completeLayer3Information");
1241 } else if (ischosen(conn_ind.userData.pdu.bssmap.handoverRequest)) {
1242 handoverRequest := true;
1243 handoverRequestPointCode := bit2int(conn_ind.calledAddress.signPointCode);
1244 log("ExpectedCreateCallback handoverRequest ", handoverRequestPointCode);
1245 } else {
1246 setverdict(fail, "N-CONNECT.ind with L3 != COMPLETE L3 nor a Handover Request");
Daniel Willmanne4ff5372018-07-05 17:35:03 +02001247 mtc.stop;
Harald Welte624f9632017-12-16 19:26:04 +01001248 return ret;
1249 }
Harald Welte624f9632017-12-16 19:26:04 +01001250
1251 for (i := 0; i < sizeof(ExpectTable); i:= i+1) {
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001252 if (handoverRequest) {
1253 log("ExpectTable[", i, "].handoverRequestPointCode = ", ExpectTable[i].handoverRequestPointCode,
1254 " ==? ", handoverRequestPointCode);
1255 if (ExpectTable[i].handoverRequestPointCode == handoverRequestPointCode) {
1256 ret := ExpectTable[i].vc_conn;
1257 log("Found Expect[", i, "] for handoverRequest handled at ", ret);
1258 return ret;
1259 } else {
1260 continue;
1261 }
1262 }
Harald Welte624f9632017-12-16 19:26:04 +01001263 if (not ispresent(ExpectTable[i].l3_payload)) {
1264 continue;
1265 }
1266 if (l3_info == ExpectTable[i].l3_payload) {
1267 ret := ExpectTable[i].vc_conn;
1268 /* release this entry to be used again */
1269 ExpectTable[i].l3_payload := omit;
1270 ExpectTable[i].vc_conn := null;
1271 log("Found Expect[", i, "] for ", l3_info, " handled at ", ret);
1272 /* return the component reference */
1273 return ret;
1274 }
1275 }
1276 setverdict(fail, "Couldn't find Expect for incoming connection ", conn_ind);
Daniel Willmanne4ff5372018-07-05 17:35:03 +02001277 mtc.stop;
Harald Welte624f9632017-12-16 19:26:04 +01001278 return ret;
1279}
Harald Welte5b027622019-04-14 23:40:17 +02001280#endif
1281
1282#ifdef RAN_EMULATION_RANAP
1283/* CreateCallback that can be used as create_cb and will use the expectation table */
1284function RanapExpectedCreateCallback(RANAP_N_CONNECT_ind conn_ind, charstring id)
1285runs on RAN_Emulation_CT return RAN_ConnHdlr {
1286 var RAN_ConnHdlr ret := null;
1287 var template (omit) octetstring l3_info;
1288 var integer i;
1289
1290 l3_info := f_ranap_extract_l3(conn_ind.userData);
1291 if (istemplatekind(l3_info, "omit")) {
1292 setverdict(fail, "N-CONNECT.ind without NAS payload");
1293 mtc.stop;
1294 return ret;
1295 }
1296
1297 for (i := 0; i < sizeof(ExpectTable); i:= i+1) {
1298 if (not ispresent(ExpectTable[i].l3_payload)) {
1299 continue;
1300 }
1301 if (valueof(l3_info) == ExpectTable[i].l3_payload) {
1302 ret := ExpectTable[i].vc_conn;
1303 /* release this entry to be used again */
1304 ExpectTable[i].l3_payload := omit;
1305 ExpectTable[i].vc_conn := null;
1306 log("Found Expect[", i, "] for ", l3_info, " handled at ", ret);
1307 /* return the component reference */
1308 return ret;
1309 }
1310 }
1311 setverdict(fail, "Couldn't find Expect for incoming connection ", conn_ind);
1312 mtc.stop;
1313 return ret;
1314}
1315#endif
Harald Welte624f9632017-12-16 19:26:04 +01001316
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001317private function f_create_expect(template octetstring l3, RAN_ConnHdlr hdlr,
1318 template integer handoverRequestPointCode := omit)
Harald Welte6811d102019-04-14 22:23:14 +02001319runs on RAN_Emulation_CT {
Harald Welte624f9632017-12-16 19:26:04 +01001320 var integer i;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001321 log("f_create_expect(l3 := ", l3, ", handoverRequest := ", handoverRequestPointCode);
Harald Welte624f9632017-12-16 19:26:04 +01001322 for (i := 0; i < sizeof(ExpectTable); i := i+1) {
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001323 if (not ispresent(ExpectTable[i].l3_payload)
1324 and not ispresent(ExpectTable[i].handoverRequestPointCode)) {
1325 if (ispresent(l3)) {
1326 ExpectTable[i].l3_payload := valueof(l3);
1327 }
1328 if (ispresent(handoverRequestPointCode)) {
1329 ExpectTable[i].handoverRequestPointCode := valueof(handoverRequestPointCode);
1330 }
Harald Welte624f9632017-12-16 19:26:04 +01001331 ExpectTable[i].vc_conn := hdlr;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001332 if (ispresent(handoverRequestPointCode)) {
1333 log("Created Expect[", i, "] for handoverRequest to be handled at ", hdlr);
1334 } else {
1335 log("Created Expect[", i, "] for ", l3, " to be handled at ", hdlr);
1336 }
Harald Welte624f9632017-12-16 19:26:04 +01001337 return;
1338 }
1339 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +02001340 testcase.stop("No space left in ExpectTable");
Harald Welte624f9632017-12-16 19:26:04 +01001341}
1342
Harald Welte6811d102019-04-14 22:23:14 +02001343private function f_create_imsi(hexstring imsi, OCT4 tmsi, RAN_ConnHdlr hdlr)
1344runs on RAN_Emulation_CT {
Harald Welte17d21152018-01-27 00:47:11 +01001345 for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) {
1346 if (not ispresent(ImsiTable[i].imsi)) {
1347 ImsiTable[i].imsi := imsi;
1348 ImsiTable[i].tmsi := tmsi;
1349 ImsiTable[i].comp_ref := hdlr;
1350 log("Created IMSI[", i, "] for ", imsi, tmsi, " to be handled at ", hdlr);
1351 return;
1352 }
1353 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +02001354 testcase.stop("No space left in ImsiTable");
Harald Welte17d21152018-01-27 00:47:11 +01001355}
1356
1357
Daniel Willmannd47106b2018-01-17 12:20:56 +01001358private function f_expect_table_init()
Harald Welte6811d102019-04-14 22:23:14 +02001359runs on RAN_Emulation_CT {
Daniel Willmannd47106b2018-01-17 12:20:56 +01001360 for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) {
1361 ExpectTable[i].l3_payload := omit;
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001362 ExpectTable[i].handoverRequestPointCode := omit;
Daniel Willmannd47106b2018-01-17 12:20:56 +01001363 }
1364}
Harald Welte624f9632017-12-16 19:26:04 +01001365
Harald Welte17d21152018-01-27 00:47:11 +01001366/* helper function for clients to register their IMSI/TMSI */
Harald Welte6811d102019-04-14 22:23:14 +02001367function f_ran_register_imsi(hexstring imsi, OCT4 tmsi)
1368runs on RAN_ConnHdlr {
1369 BSSAP_PROC.call(RAN_register_imsi:{imsi, tmsi, self}) {
1370 [] BSSAP_PROC.getreply(RAN_register_imsi:{?,?,?}) {};
Harald Welte17d21152018-01-27 00:47:11 +01001371 }
1372}
1373
Harald Welte475a2c12019-05-02 19:05:48 +02001374#ifdef RAN_EMULATION_RANAP
1375/* expect a IuReleaseCommand; Confirm that; expect SCCP-level N-DISCONNET.ind */
1376altstep as_iu_release_compl_disc(float t := 5.0) runs on RAN_ConnHdlr {
1377 var RANAP_PDU ranap;
1378 [] BSSAP.receive(tr_RANAP_IuReleaseCommand(?)) {
1379 BSSAP.send(ts_RANAP_IuReleaseComplete);
1380 alt {
1381 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
1382 setverdict(pass);
1383 }
1384 [] BSSAP.receive {
1385 setverdict(fail, "Unexpected RANAP while waiting for SCCP Release ");
1386 mtc.stop;
1387 }
1388 }
1389 }
1390 [] BSSAP.receive(RANAP_PDU:?) -> value ranap{
1391 setverdict(fail, "Unexpected RANAP while waiting for IuReleaseCommand", ranap);
1392 mtc.stop;
1393 }
1394}
1395#endif
1396
Harald Welte17d21152018-01-27 00:47:11 +01001397
Harald Welte365f4ed2017-11-23 00:00:43 +01001398}