module MSC_ConnectionHandler { 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, 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; } } } } }