| module MSC_ConnectionHandler { |
| |
| /* (C) 2017-2019 Harald Welte <laforge@gnumonks.org> |
| * All rights reserved. |
| * |
| * Released under the terms of GNU General Public License, Version 2 or |
| * (at your option) any later version. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| */ |
| |
| import from General_Types all; |
| import from Osmocom_Types all; |
| import from SCCPasp_Types all; |
| import from BSSAP_Types all; |
| import from BSSAP_CodecPort all; |
| import from RAN_Emulation all; |
| import from BSSMAP_Templates all; |
| |
| import from MGCP_Types all; |
| import from MGCP_Templates all; |
| import from SDP_Types all; |
| |
| /* this component represents a single subscriber connection at the MSC. |
| * There is a 1:1 mapping between SCCP connections and RAN_ConnHdlr components. |
| * We inherit all component variables, ports, functions, ... from RAN_ConnHdlr */ |
| type component MSC_ConnHdlr extends RAN_ConnHdlr { |
| /* SCCP Connecction Identifier for the underlying SCCP connection */ |
| var integer g_sccp_conn_id; |
| |
| var MSC_State g_state := MSC_STATE_NONE; |
| var MgcpEndpoint g_ep_name; |
| var MgcpCallId g_call_id; |
| var MgcpConnectionId g_mgcp_conn_id; |
| } |
| |
| /* Callback function from general BSSMAP_Emulation whenever a new incoming |
| * SCCP connection arrivces. Must create + start a new component */ |
| private function CreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) |
| runs on RAN_Emulation_CT return RAN_ConnHdlr { |
| var MSC_ConnHdlr vc_conn; |
| /* Create a new RAN_ConnHdlr component */ |
| vc_conn := MSC_ConnHdlr.create(g_ran_id & "-Conn-" & int2str(conn_ind.connectionId)); |
| /* connect it to the port */ |
| connect(vc_conn:BSSAP, self:CLIENT); |
| /* start it */ |
| vc_conn.start(MSC_ConnectionHandler.main(conn_ind.connectionId, g_next_e1_ts)); |
| /* increment next E1 timeslot */ |
| g_next_e1_ts := g_next_e1_ts + 1; |
| return vc_conn; |
| } |
| |
| /* Callback function from general BSSMAP_Emulation whenever a connectionless |
| * BSSMAP message arrives. Can retunr a PDU_BSSAP that should be sent in return */ |
| private function UnitdataCallback(PDU_BSSAP bssap) |
| runs on RAN_Emulation_CT return template PDU_BSSAP { |
| var template PDU_BSSAP resp := omit; |
| |
| if (match(bssap, tr_BSSMAP_Reset)) { |
| resp := ts_BSSMAP_ResetAck; |
| } |
| |
| return resp; |
| } |
| |
| const RanOps MSC_RanOps := { |
| create_cb := refers(CreateCallback), |
| unitdata_cb := refers(UnitdataCallback), |
| decode_dtap := false, |
| role_ms := false, |
| protocol := RAN_PROTOCOL_BSSAP, |
| transport := BSSAP_TRANSPORT_AoIP, |
| use_osmux := false, |
| sccp_addr_local := omit, |
| sccp_addr_peer := omit |
| } |
| |
| type enumerated MSC_State { |
| MSC_STATE_NONE, |
| MSC_STATE_WAIT_ASS_COMPL, |
| MSC_STATE_WAIT_CRCX_ACK, |
| MSC_STATE_WAIT_MDCX_ACK, |
| MSC_STATE_WAIT_CLEAR_COMPL, |
| MSC_STATE_WAIT_DLCX_ACK |
| } |
| |
| /* main function processing various incoming events */ |
| function main(integer connection_id, integer e1_timeslot) runs on MSC_ConnHdlr { |
| var MgcpResponse mgcp_rsp; |
| timer T := 5.0; |
| |
| g_sccp_conn_id := connection_id; |
| g_call_id := f_mgcp_alloc_call_id(); |
| g_ep_name := hex2str(int2hex(e1_timeslot, 1)) & "@mgw"; |
| |
| /* we just accepted an incoming SCCP connection, start guard timer */ |
| T.start; |
| |
| while (true) { |
| var PDU_BSSAP bssap; |
| alt { |
| /* new SCCP-level connection indication from BSC */ |
| [g_state == MSC_STATE_NONE] BSSAP.receive(tr_BSSMAP_ComplL3) -> value bssap { |
| /* respond with ASSIGNMENT CMD */ |
| g_state := MSC_STATE_WAIT_ASS_COMPL; |
| BSSAP.send(ts_BSSMAP_AssignmentReq(ts_BSSMAP_IE_CIC(0, e1_timeslot))); |
| } |
| [g_state == MSC_STATE_WAIT_ASS_COMPL] BSSAP.receive(tr_BSSMAP_AssignmentComplete(?,*)) { |
| /* FIXME: Send MGCP CRCX */ |
| g_state := MSC_STATE_WAIT_CRCX_ACK; |
| var MgcpTransId trans_id := f_mgcp_alloc_tid(); |
| //template SDP_Message sdp := omit; |
| BSSAP.send(ts_CRCX(trans_id, g_ep_name, "recvonly", g_call_id)); //, sdp)); |
| } |
| /* |
| [] BSSAP.receive(tr_BSSMAP_AssignmentFail) { |
| } |
| */ |
| |
| /* receive CRCX ACK: transmit MDCX */ |
| [g_state == MSC_STATE_WAIT_CRCX_ACK] BSSAP.receive(tr_CRCX_ACK) -> value mgcp_rsp { |
| /* extract connection ID */ |
| g_mgcp_conn_id := f_MgcpResp_extract_conn_id(mgcp_rsp); |
| g_state := MSC_STATE_WAIT_MDCX_ACK; |
| var MgcpTransId trans_id := f_mgcp_alloc_tid(); |
| BSSAP.send(ts_MDCX(trans_id, g_ep_name, "sendrecv", g_call_id, g_mgcp_conn_id)); |
| } |
| |
| /* receive MDCX ACK: wait + transmit CLEAR COMMAND */ |
| [g_state == MSC_STATE_WAIT_MDCX_ACK] BSSAP.receive(tr_CRCX_ACK) -> value mgcp_rsp { |
| g_state := MSC_STATE_WAIT_CLEAR_COMPL |
| BSSAP.send(ts_BSSMAP_ClearCommand(9)); /* Cause: call control */ |
| } |
| |
| /* CLEAR COMPLETE from BSS (response to CLEAR COMMAND) */ |
| [g_state == MSC_STATE_WAIT_CLEAR_COMPL] BSSAP.receive(tr_BSSMAP_ClearComplete) { |
| /* send DLCX */ |
| g_state := MSC_STATE_WAIT_DLCX_ACK; |
| var MgcpTransId trans_id := f_mgcp_alloc_tid(); |
| BSSAP.send(ts_DLCX(trans_id, g_ep_name, g_call_id)); |
| } |
| |
| [g_state == MSC_STATE_WAIT_DLCX_ACK] BSSAP.receive(tr_DLCX_ACK) { |
| BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ); |
| setverdict(pass); |
| self.stop; |
| } |
| |
| /* TODO: CLEAR REQUEST from BSS */ |
| |
| [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) { |
| setverdict(fail); |
| self.stop; |
| } |
| |
| [] BSSAP.receive(PDU_BSSAP:?) -> value bssap { |
| log("Received unhandled SCCP-CO: ", bssap); |
| } |
| |
| /* Guard timer has expired, close connection */ |
| [] T.timeout { |
| BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ); |
| setverdict(fail, "Timeout of guard timer"); |
| self.stop; |
| } |
| |
| } |
| } |
| } |
| |
| } |