blob: 290bf3278350a9592c251c2f68c0be8d5620216f [file] [log] [blame]
Harald Welte365f4ed2017-11-23 00:00:43 +01001module BSSMAP_Emulation {
2
Harald Welteb3414b22017-11-23 18:22:10 +01003import from SCCP_Emulation all;
Harald Welte365f4ed2017-11-23 00:00:43 +01004import from SCCPasp_Types all;
5import from BSSAP_Types all;
6import from BSSMAP_Templates all;
Harald Weltec82eef42017-11-24 20:40:12 +01007import from MGCP_Types all;
8import from MGCP_Templates all;
9import from IPA_Emulation all;
Harald Welte365f4ed2017-11-23 00:00:43 +010010
11/* General "base class" component definition, of which specific implementations
12 * derive themselves by means of the "extends" feature */
13type component BSSAP_ConnHdlr {
14 /* port towards MSC Emulator core / SCCP connection dispatchar */
15 port BSSAP_Conn_PT BSSAP;
16}
17
18/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */
19type enumerated BSSAP_Conn_Prim {
20 /* SCCP tell us that connection was released */
21 MSC_CONN_PRIM_DISC_IND,
22 /* we tell SCCP to release connection */
23 MSC_CONN_PRIM_DISC_REQ
24}
25
Harald Welteb3414b22017-11-23 18:22:10 +010026type record BSSAP_Conn_Req {
27 SCCP_PAR_Address addr_peer,
28 SCCP_PAR_Address addr_own,
29 PDU_BSSAP bssap
30}
31
Harald Welte365f4ed2017-11-23 00:00:43 +010032/* port between individual per-connection components and this dispatcher */
33type port BSSAP_Conn_PT message {
Harald Weltec82eef42017-11-24 20:40:12 +010034 inout PDU_BSSAP, BSSAP_Conn_Prim, BSSAP_Conn_Req, MgcpCommand, MgcpResponse;
Harald Welte365f4ed2017-11-23 00:00:43 +010035} with { extension "internal" };
36
37
38/* represents a single BSSAP connection over SCCP */
39type record ConnectionData {
40 /* reference to the instance of the per-connection component */
41 BSSAP_ConnHdlr comp_ref,
Harald Weltec82eef42017-11-24 20:40:12 +010042 integer sccp_conn_id,
43 /* most recent MGCP transaction ID (Used on MSC side) */
44 MgcpTransId mgcp_trans_id optional,
45 /* CIC that has been used for voice of this channel (BSC side) */
46 integer cic optional
Harald Welte365f4ed2017-11-23 00:00:43 +010047}
48
49type component BSSMAP_Emulation_CT {
50 /* SCCP port on the bottom side, using ASP primitives */
51 port SCCPasp_PT SCCP;
52 /* BSSAP port to the per-connection clients */
53 port BSSAP_Conn_PT CLIENT;
Harald Weltec82eef42017-11-24 20:40:12 +010054 /* MGCP port */
55 port IPA_MGCP_PT MGCP;
Harald Welte365f4ed2017-11-23 00:00:43 +010056
57 /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
58 var ConnectionData ConnectionTable[16];
Harald Welte66fecd42017-11-24 23:53:23 +010059
60 var integer g_next_e1_ts := 1;
Harald Welte365f4ed2017-11-23 00:00:43 +010061};
62
Harald Welteb3414b22017-11-23 18:22:10 +010063private function f_conn_id_known(integer sccp_conn_id)
64runs on BSSMAP_Emulation_CT return boolean {
65 var integer i;
66 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
67 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id){
68 return true;
69 }
70 }
71 return false;
72}
73
74private function f_comp_known(BSSAP_ConnHdlr client)
75runs on BSSMAP_Emulation_CT return boolean {
76 var integer i;
77 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
78 if (ConnectionTable[i].comp_ref == client) {
79 return true;
80 }
81 }
82 return false;
83}
Harald Welte365f4ed2017-11-23 00:00:43 +010084
85/* resolve component reference by connection ID */
86private function f_comp_by_conn_id(integer sccp_conn_id)
87runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr {
88 var integer i;
89 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
90 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
91 return ConnectionTable[i].comp_ref;
92 }
93 }
94 log("BSSMAP Connection table not found by SCCP Connection ID ", sccp_conn_id);
95 self.stop;
96}
97
Harald Weltec82eef42017-11-24 20:40:12 +010098/* resolve component reference by CIC */
99private function f_comp_by_mgcp_tid(MgcpTransId tid)
100runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr {
101 var integer i;
102 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
103 if (ConnectionTable[i].mgcp_trans_id == tid) {
104 return ConnectionTable[i].comp_ref;
105 }
106 }
107 log("BSSMAP Connection table not found by MGCP Transaction ID ", tid);
108 self.stop;
109}
110
111private function f_comp_store_mgcp_tid(BSSAP_ConnHdlr client, MgcpTransId tid)
112runs on BSSMAP_Emulation_CT {
113 var integer i;
114 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
115 if (ConnectionTable[i].comp_ref == client) {
116 ConnectionTable[i].mgcp_trans_id := tid;
117 return;
118 }
119 }
120 log("BSSMAP Connection table not found by component ", client);
121 self.stop;
122}
123
124private function f_comp_by_cic(integer cic)
125runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr {
126 var integer i;
127 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
128 if (ConnectionTable[i].cic == cic) {
129 return ConnectionTable[i].comp_ref;
130 }
131 }
132 log("BSSMAP Connection table not found by CIC ", cic);
133 self.stop;
134}
135
136private function f_comp_store_cic(BSSAP_ConnHdlr client, integer cic)
137runs on BSSMAP_Emulation_CT {
138 var integer i;
139 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
140 if (ConnectionTable[i].comp_ref == client) {
141 ConnectionTable[i].cic := cic;
142 return;
143 }
144 }
145 log("BSSMAP Connection table not found by component ", client);
146}
147
Harald Welte365f4ed2017-11-23 00:00:43 +0100148/* resolve connection ID by component reference */
149private function f_conn_id_by_comp(BSSAP_ConnHdlr client)
150runs on BSSMAP_Emulation_CT return integer {
151 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
152 if (ConnectionTable[i].comp_ref == client) {
153 return ConnectionTable[i].sccp_conn_id;
154 }
155 }
156 log("BSSMAP Connection table not found by component ", client);
157 self.stop;
158}
159
Harald Welteb3414b22017-11-23 18:22:10 +0100160private function f_gen_conn_id()
161runs on BSSMAP_Emulation_CT return integer {
162 var integer conn_id;
163
164 do {
165 conn_id := float2int(rnd()*SCCP_Emulation.tsp_max_ConnectionId);
166 } while (f_conn_id_known(conn_id) == true);
167
168 return conn_id;
169}
170
Harald Welte365f4ed2017-11-23 00:00:43 +0100171private function f_conn_table_init()
172runs on BSSMAP_Emulation_CT {
173 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
174 ConnectionTable[i].comp_ref := null;
175 ConnectionTable[i].sccp_conn_id := -1;
Harald Weltec82eef42017-11-24 20:40:12 +0100176 ConnectionTable[i].mgcp_trans_id := omit;
177 ConnectionTable[i].cic := omit;
Harald Welte365f4ed2017-11-23 00:00:43 +0100178 }
179}
180
181private function f_conn_table_add(BSSAP_ConnHdlr comp_ref, integer sccp_conn_id)
182runs on BSSMAP_Emulation_CT {
183 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
184 if (ConnectionTable[i].sccp_conn_id == -1) {
185 ConnectionTable[i].comp_ref := comp_ref;
186 ConnectionTable[i].sccp_conn_id := sccp_conn_id;
Harald Welteb3414b22017-11-23 18:22:10 +0100187 log("Added conn table entry ", i, comp_ref, sccp_conn_id);
Harald Welte365f4ed2017-11-23 00:00:43 +0100188 return;
189 }
190 }
191 log("BSSMAP Connection table full!");
192 self.stop;
193}
194
195private function f_conn_table_del(integer sccp_conn_id)
196runs on BSSMAP_Emulation_CT {
197 for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
198 if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
Harald Welteb3414b22017-11-23 18:22:10 +0100199 log("Deleted conn table entry ", i,
200 ConnectionTable[i].comp_ref, sccp_conn_id);
Harald Welte365f4ed2017-11-23 00:00:43 +0100201 ConnectionTable[i].sccp_conn_id := -1;
202 ConnectionTable[i].comp_ref := null;
203 }
204 }
205 log("BSSMAP Connection table attempt to delete non-existant ", sccp_conn_id);
206 self.stop;
207}
208
209/* handle (optional) userData portion of various primitives and dispatch it to the client */
Harald Welte5cc4aa22017-11-23 18:51:28 +0100210private function f_handle_userData(BSSAP_ConnHdlr client, octetstring userdata)
Harald Welte365f4ed2017-11-23 00:00:43 +0100211runs on BSSMAP_Emulation_CT {
Harald Welte365f4ed2017-11-23 00:00:43 +0100212 /* decode + send decoded BSSAP to client */
213 var PDU_BSSAP bssap := dec_PDU_BSSAP(valueof(userdata));
Harald Welte1b2748e2017-11-24 23:40:16 +0100214
215 /* BSC Side: If this is an assignment command, store CIC */
216 if (ischosen(bssap.pdu.bssmap.assignmentRequest) and
217 ispresent(bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode)) {
218 var BSSMAP_IE_CircuitIdentityCode cic_ie :=
219 bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode;
220 var integer cic := (oct2int(cic_ie.cicHigh) * 256) + oct2int(cic_ie.cicLow);
221 f_comp_store_cic(client, cic);
222 }
223
Harald Welte365f4ed2017-11-23 00:00:43 +0100224 CLIENT.send(bssap) to client;
225}
226
227/* call-back type, to be provided by specific implementation; called when new SCCP connection
228 * arrives */
229type function BssmapCreateCallback(ASP_SCCP_N_CONNECT_ind conn_ind)
230runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr;
231
232type function BssmapUnitdataCallback(PDU_BSSAP bssap)
233runs on BSSMAP_Emulation_CT return template PDU_BSSAP;
234
235type record BssmapOps {
236 BssmapCreateCallback create_cb,
237 BssmapUnitdataCallback unitdata_cb
238}
239
240function main(BssmapOps ops) runs on BSSMAP_Emulation_CT {
241
242 f_conn_table_init();
243
244 while (true) {
245 var ASP_SCCP_N_UNITDATA_ind ud_ind;
246 var ASP_SCCP_N_CONNECT_ind conn_ind;
Harald Welteb3414b22017-11-23 18:22:10 +0100247 var ASP_SCCP_N_CONNECT_cfm conn_cfm;
Harald Welte365f4ed2017-11-23 00:00:43 +0100248 var ASP_SCCP_N_DATA_ind data_ind;
249 var ASP_SCCP_N_DISCONNECT_ind disc_ind;
Harald Welteb3414b22017-11-23 18:22:10 +0100250 var BSSAP_Conn_Req creq;
Harald Welte365f4ed2017-11-23 00:00:43 +0100251 var BSSAP_ConnHdlr vc_conn;
252 var PDU_BSSAP bssap;
Harald Weltec82eef42017-11-24 20:40:12 +0100253 var MgcpCommand mgcp_req;
254 var MgcpResponse mgcp_resp;
Harald Welte365f4ed2017-11-23 00:00:43 +0100255
256 alt {
257 /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
258 [] SCCP.receive(ASP_SCCP_N_UNITDATA_ind:?) -> value ud_ind {
259 /* Connectionless Procedures like RESET */
260 var template PDU_BSSAP resp;
261 bssap := dec_PDU_BSSAP(ud_ind.userData);
262 resp := ops.unitdata_cb.apply(bssap);
263 if (isvalue(resp)) {
264 var octetstring resp_ud := enc_PDU_BSSAP(valueof(resp));
265 SCCP.send(t_ASP_N_UNITDATA_req(ud_ind.callingAddress,
266 ud_ind.calledAddress, omit,
267 omit, resp_ud, omit));
268 }
269 }
270
271 /* SCCP -> Client: new connection from BSC */
272 [] SCCP.receive(ASP_SCCP_N_CONNECT_ind:?) -> value conn_ind {
273 vc_conn := ops.create_cb.apply(conn_ind);
274 /* store mapping between client components and SCCP connectionId */
275 f_conn_table_add(vc_conn, conn_ind.connectionId);
276 /* handle user payload */
277 f_handle_userData(vc_conn, conn_ind.userData);
278 /* confirm connection establishment */
279 SCCP.send(t_ASP_N_CONNECT_res(omit, omit, omit, omit, conn_ind.connectionId, omit));
280 }
281
282 /* SCCP -> Client: connection-oriented data in existing connection */
283 [] SCCP.receive(ASP_SCCP_N_DATA_ind:?) -> value data_ind {
284 vc_conn := f_comp_by_conn_id(data_ind.connectionId);
Harald Welte5cc4aa22017-11-23 18:51:28 +0100285 if (ispresent(data_ind.userData)) {
286 f_handle_userData(vc_conn, data_ind.userData);
287 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100288 }
289
290 /* SCCP -> Client: disconnect of an existing connection */
291 [] SCCP.receive(ASP_SCCP_N_DISCONNECT_ind:?) -> value disc_ind {
292 vc_conn := f_comp_by_conn_id(disc_ind.connectionId);
Harald Welte5cc4aa22017-11-23 18:51:28 +0100293 if (ispresent(disc_ind.userData)) {
294 f_handle_userData(vc_conn, disc_ind.userData);
295 }
Harald Welte365f4ed2017-11-23 00:00:43 +0100296 /* notify client about termination */
297 var BSSAP_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND;
298 CLIENT.send(prim) to vc_conn;
299 f_conn_table_del(disc_ind.connectionId);
300 /* TOOD: return confirm to other side? */
301 }
302
Harald Welteb3414b22017-11-23 18:22:10 +0100303 /* SCCP -> Client: connection confirm for outbound connection */
304 [] SCCP.receive(ASP_SCCP_N_CONNECT_cfm:?) -> value conn_cfm {
305 /* handle user payload */
Harald Welte5cc4aa22017-11-23 18:51:28 +0100306 if (ispresent(conn_cfm.userData)) {
307 f_handle_userData(vc_conn, conn_cfm.userData);
308 }
Harald Welteb3414b22017-11-23 18:22:10 +0100309 }
310
Harald Welte365f4ed2017-11-23 00:00:43 +0100311 /* Disconnect request client -> SCCP */
312 [] CLIENT.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
313 var integer conn_id := f_conn_id_by_comp(vc_conn);
314 SCCP.send(t_ASP_N_DISCONNECT_req(omit, 0, omit, conn_id, omit));
315 f_conn_table_del(conn_id);
316 }
317
318 /* BSSAP from client -> SCCP */
Harald Welteb3414b22017-11-23 18:22:10 +0100319 [] CLIENT.receive(BSSAP_Conn_Req:?) -> value creq sender vc_conn {
320 var integer conn_id;
321 /* encode + send to dispatcher */
322 var octetstring userdata := enc_PDU_BSSAP(creq.bssap);
323
324 if (f_comp_known(vc_conn) == false) {
325 /* unknown client, create new connection */
326 conn_id := f_gen_conn_id();
327
328 /* store mapping between client components and SCCP connectionId */
329 f_conn_table_add(vc_conn, conn_id);
330
331 SCCP.send(t_ASP_N_CONNECT_req(creq.addr_peer, creq.addr_own, omit, omit,
332 userdata, conn_id, omit));
333 } else {
334 /* known client, send via existing connection */
335 conn_id := f_conn_id_by_comp(vc_conn);
336 SCCP.send(t_ASP_N_DATA_req(userdata, conn_id, omit));
337 }
338
339 }
340
Harald Welte365f4ed2017-11-23 00:00:43 +0100341 [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn {
342 var integer conn_id := f_conn_id_by_comp(vc_conn);
Harald Welteb3414b22017-11-23 18:22:10 +0100343 /* encode + send it to dispatcher */
Harald Welte365f4ed2017-11-23 00:00:43 +0100344 var octetstring userdata := enc_PDU_BSSAP(bssap);
345 SCCP.send(t_ASP_N_DATA_req(userdata, conn_id, omit));
346 }
347
Harald Weltec82eef42017-11-24 20:40:12 +0100348 /* Handling of MGCP in IPA SCCPLite case. This predates 3GPP AoIP
349 * and uses a MGCP session in parallel to BSSAP. BSSAP uses CIC
350 * as usual, and MGCP uses "CIC@mgw" endpoint naming, where CIC
351 * is printed as hex string, e.g. a@mgw for CIC 10 */
352
353 /* CLIENT -> MGCP */
354 [] CLIENT.receive(MgcpCommand:?) -> value mgcp_req sender vc_conn {
355 /* MGCP request from Handler (we're MSC) */
356 /* store the transaction ID we've seen */
357 f_comp_store_mgcp_tid(vc_conn, mgcp_req.line.trans_id);
358 /* simply forward any MGCP from the client to the port */
359 MGCP.send(mgcp_req);
360 }
361 [] CLIENT.receive(MgcpResponse:?) -> value mgcp_resp sender vc_conn {
362 /* MGCP response from Handler (we're BSC/MGW) */
363 /* simply forward any MGCP from the client to the port */
364 MGCP.send(mgcp_resp);
365 }
366
367 /* MGCP -> CLIENT */
368 [] MGCP.receive(MgcpCommand:?) -> value mgcp_req {
369 /* MGCP request from network side (we're BSC/MGW) */
370 /* Extract CIC from local part of endpoint name */
371 var integer cic := f_mgcp_ep_extract_cic(mgcp_req.line.ep);
372 /* Resolve the vc_conn by the CIC */
373 vc_conn := f_comp_by_cic(cic);
374 CLIENT.send(mgcp_req) to vc_conn;
375 }
376 [] MGCP.receive(MgcpResponse:?) -> value mgcp_resp {
377 /* MGCP response from network side (we're MSC) */
378 /* Resolve the vc_conn by the transaction ID */
379 vc_conn := f_comp_by_mgcp_tid(mgcp_resp.line.trans_id);
380 CLIENT.send(mgcp_resp) to vc_conn;
381 }
382
Harald Welte365f4ed2017-11-23 00:00:43 +0100383 }
384 }
385}
386
Harald Weltec82eef42017-11-24 20:40:12 +0100387private function f_mgcp_ep_extract_cic(charstring inp) return integer {
Harald Welte525a9c12017-11-24 23:40:41 +0100388 var charstring local_part := regexp(inp, "(*)@*", 0);
Harald Weltec82eef42017-11-24 20:40:12 +0100389 return hex2int(str2hex(local_part));
390
391}
Harald Welte365f4ed2017-11-23 00:00:43 +0100392
393}