From 365f4edbd8dceb0048b15b43299511db104443da Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 23 Nov 2017 00:00:43 +0100 Subject: [PATCH] IPA/MSC/BSC Emulation: Framework for handling multiple SCCP connections --- ipa/MSC_ConnectionHandler.ttcn | 86 ++++++++++++++ library/BSSMAP_Emulation.ttcn | 206 +++++++++++++++++++++++++++++++++ library/BSSMAP_Templates.ttcn | 71 ++++++++++++ library/L3_Templates.ttcn | 4 +- 4 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 ipa/MSC_ConnectionHandler.ttcn create mode 100644 library/BSSMAP_Emulation.ttcn diff --git a/ipa/MSC_ConnectionHandler.ttcn b/ipa/MSC_ConnectionHandler.ttcn new file mode 100644 index 000000000..0d7cd20bd --- /dev/null +++ b/ipa/MSC_ConnectionHandler.ttcn @@ -0,0 +1,86 @@ +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 BSSMAP_Emulation all; +import from BSSMAP_Templates all; + +/* this component represents a single subscriber connection at the MSC. + * There is a 1:1 mapping between SCCP connections and BSSAP_ConnHdlr components. + * We inherit all component variables, ports, functions, ... from BSSAP_ConnHdlr */ +type component MSC_ConnHdlr extends BSSAP_ConnHdlr { + /* E1 timeslot that is allocated to this component/connection */ + var uint5_t g_e1_timeslot; + /* SCCP Connecction Identifier for the underlying SCCP connection */ + var integer g_sccp_conn_id; +} + +/* Callback function from general BSSMAP_Emulation whenever a new incoming + * SCCP connection arrivces. Must create + start a new component */ +private function CreateCallback(ASP_SCCP_N_CONNECT_ind conn_ind) +runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr { + var MSC_ConnHdlr vc_conn; + /* Create a new BSSAP_ConnHdlr component */ + vc_conn := MSC_ConnHdlr.create; + /* connect it to the port */ + connect(vc_conn:BSSAP, self:CLIENT); + /* start it */ + vc_conn.start(MSC_ConnectionHandler.main(conn_ind.connectionId, conn_ind.connectionId)); + 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 BSSMAP_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 BssmapOps MSC_BssmapOps := { + create_cb := refers(CreateCallback), + unitdata_cb := refers(UnitdataCallback) +} + +/* main function processing various incoming events */ +function main(integer connection_id, integer timeslot) runs on MSC_ConnHdlr { + g_sccp_conn_id := connection_id; + g_e1_timeslot := timeslot + + while (true) { + var PDU_BSSAP bssap; + alt { + /* new SCCP-level connection indication from BSC */ + [] BSSAP.receive(tr_BSSMAP_ComplL3) -> value bssap { + /* respond with ASSIGNMENT CMD */ + BSSAP.send(ts_BSSMAP_AssignmentCmd(0, g_e1_timeslot)); + /* FIXME: Send MGCP */ + } + /* + [] BSSAP.receive(tr_BSSMAP_AssignmentCompl) { + } + [] BSSAP.receive(tr_BSSMAP_AssignmentFail) { + } + */ + /* TODO: CLEAR REQUEST from BSS */ + /* TODO: CLEAR COMPLETE from BSS (response to CLEAR COMMAND) */ + + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) { + self.stop; + } + + [] BSSAP.receive(PDU_BSSAP:?) -> value bssap { + log("Received unhandled SCCP-CO: ", bssap); + } + } + } +} + +} diff --git a/library/BSSMAP_Emulation.ttcn b/library/BSSMAP_Emulation.ttcn new file mode 100644 index 000000000..bcc0e1128 --- /dev/null +++ b/library/BSSMAP_Emulation.ttcn @@ -0,0 +1,206 @@ +module BSSMAP_Emulation { + +import from SCCPasp_Types all; +import from BSSAP_Types all; +import from BSSMAP_Templates all; +//import from MSC_ConnectionHandler all; + +/* General "base class" component definition, of which specific implementations + * derive themselves by means of the "extends" feature */ +type component BSSAP_ConnHdlr { + /* port towards MSC Emulator core / SCCP connection dispatchar */ + port BSSAP_Conn_PT BSSAP; +} + +/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */ +type enumerated BSSAP_Conn_Prim { + /* SCCP tell us that connection was released */ + MSC_CONN_PRIM_DISC_IND, + /* we tell SCCP to release connection */ + MSC_CONN_PRIM_DISC_REQ +} + +/* port between individual per-connection components and this dispatcher */ +type port BSSAP_Conn_PT message { + inout PDU_BSSAP; + inout BSSAP_Conn_Prim; +} with { extension "internal" }; + + +/* represents a single BSSAP connection over SCCP */ +type record ConnectionData { + /* reference to the instance of the per-connection component */ + BSSAP_ConnHdlr comp_ref, + integer sccp_conn_id +} + +type component BSSMAP_Emulation_CT { + /* SCCP port on the bottom side, using ASP primitives */ + port SCCPasp_PT SCCP; + /* BSSAP port to the per-connection clients */ + port BSSAP_Conn_PT CLIENT; + + /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */ + var ConnectionData ConnectionTable[16]; +}; + + +/* resolve component reference by connection ID */ +private function f_comp_by_conn_id(integer sccp_conn_id) +runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { + return ConnectionTable[i].comp_ref; + } + } + log("BSSMAP Connection table not found by SCCP Connection ID ", sccp_conn_id); + self.stop; +} + +/* resolve connection ID by component reference */ +private function f_conn_id_by_comp(BSSAP_ConnHdlr client) +runs on BSSMAP_Emulation_CT return integer { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + return ConnectionTable[i].sccp_conn_id; + } + } + log("BSSMAP Connection table not found by component ", client); + self.stop; +} + +private function f_conn_table_init() +runs on BSSMAP_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + ConnectionTable[i].comp_ref := null; + ConnectionTable[i].sccp_conn_id := -1; + } +} + +private function f_conn_table_add(BSSAP_ConnHdlr comp_ref, integer sccp_conn_id) +runs on BSSMAP_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == -1) { + ConnectionTable[i].comp_ref := comp_ref; + ConnectionTable[i].sccp_conn_id := sccp_conn_id; + return; + } + } + log("BSSMAP Connection table full!"); + self.stop; +} + +private function f_conn_table_del(integer sccp_conn_id) +runs on BSSMAP_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { + ConnectionTable[i].sccp_conn_id := -1; + ConnectionTable[i].comp_ref := null; + } + } + log("BSSMAP Connection table attempt to delete non-existant ", sccp_conn_id); + self.stop; +} + +/* handle (optional) userData portion of various primitives and dispatch it to the client */ +private function f_handle_userData(BSSAP_ConnHdlr client, template octetstring userdata) +runs on BSSMAP_Emulation_CT { + if (not isvalue(userdata)) { + return; + } + + /* decode + send decoded BSSAP to client */ + var PDU_BSSAP bssap := dec_PDU_BSSAP(valueof(userdata)); + CLIENT.send(bssap) to client; +} + +/* call-back type, to be provided by specific implementation; called when new SCCP connection + * arrives */ +type function BssmapCreateCallback(ASP_SCCP_N_CONNECT_ind conn_ind) +runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr; + +type function BssmapUnitdataCallback(PDU_BSSAP bssap) +runs on BSSMAP_Emulation_CT return template PDU_BSSAP; + +type record BssmapOps { + BssmapCreateCallback create_cb, + BssmapUnitdataCallback unitdata_cb +} + +function main(BssmapOps ops) runs on BSSMAP_Emulation_CT { + + f_conn_table_init(); + + while (true) { + var ASP_SCCP_N_UNITDATA_ind ud_ind; + var ASP_SCCP_N_CONNECT_ind conn_ind; + var ASP_SCCP_N_DATA_ind data_ind; + var ASP_SCCP_N_DISCONNECT_ind disc_ind; + var BSSAP_ConnHdlr vc_conn; + var PDU_BSSAP bssap; + + alt { + /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */ + [] SCCP.receive(ASP_SCCP_N_UNITDATA_ind:?) -> value ud_ind { + /* Connectionless Procedures like RESET */ + var template PDU_BSSAP resp; + bssap := dec_PDU_BSSAP(ud_ind.userData); + resp := ops.unitdata_cb.apply(bssap); + if (isvalue(resp)) { + var octetstring resp_ud := enc_PDU_BSSAP(valueof(resp)); + SCCP.send(t_ASP_N_UNITDATA_req(ud_ind.callingAddress, + ud_ind.calledAddress, omit, + omit, resp_ud, omit)); + } + } + + /* SCCP -> Client: new connection from BSC */ + [] SCCP.receive(ASP_SCCP_N_CONNECT_ind:?) -> value conn_ind { + vc_conn := ops.create_cb.apply(conn_ind); + /* store mapping between client components and SCCP connectionId */ + f_conn_table_add(vc_conn, conn_ind.connectionId); + /* handle user payload */ + f_handle_userData(vc_conn, conn_ind.userData); + /* confirm connection establishment */ + SCCP.send(t_ASP_N_CONNECT_res(omit, omit, omit, omit, conn_ind.connectionId, omit)); + } + + /* SCCP -> Client: connection-oriented data in existing connection */ + [] SCCP.receive(ASP_SCCP_N_DATA_ind:?) -> value data_ind { + vc_conn := f_comp_by_conn_id(data_ind.connectionId); + f_handle_userData(vc_conn, conn_ind.userData); + } + + /* SCCP -> Client: disconnect of an existing connection */ + [] SCCP.receive(ASP_SCCP_N_DISCONNECT_ind:?) -> value disc_ind { + vc_conn := f_comp_by_conn_id(disc_ind.connectionId); + f_handle_userData(vc_conn, disc_ind.userData); + /* notify client about termination */ + var BSSAP_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND; + CLIENT.send(prim) to vc_conn; + f_conn_table_del(disc_ind.connectionId); + /* TOOD: return confirm to other side? */ + } + + /* Disconnect request client -> SCCP */ + [] CLIENT.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn { + var integer conn_id := f_conn_id_by_comp(vc_conn); + SCCP.send(t_ASP_N_DISCONNECT_req(omit, 0, omit, conn_id, omit)); + f_conn_table_del(conn_id); + } + + /* BSSAP from client -> SCCP */ + [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn { + var integer conn_id := f_conn_id_by_comp(vc_conn); + /* encode + send to dispatcher */ + var octetstring userdata := enc_PDU_BSSAP(bssap); + SCCP.send(t_ASP_N_DATA_req(userdata, conn_id, omit)); + } + + } + } +} + + +} diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn index aea1e011f..b030072ee 100644 --- a/library/BSSMAP_Templates.ttcn +++ b/library/BSSMAP_Templates.ttcn @@ -44,6 +44,18 @@ template (value) PDU_BSSAP ts_BSSMAP_Reset(BssmapCause cause) modifies ts_BSSAP_ } } +template PDU_BSSAP tr_BSSMAP_Reset modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + reset := { + messageType := '30'O, + cause := ?, + a_InterfaceSelectorForReset := * + } + } + } +} + template (value) PDU_BSSAP ts_BSSMAP_ResetAck modifies ts_BSSAP_BSSMAP := { pdu := { bssmap := { @@ -141,6 +153,15 @@ modifies ts_BSSAP_BSSMAP := { } } +template PDU_BSSAP tr_BSSMAP_ComplL3 modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + completeLayer3Information := ? + } + } +} + + template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_IE_CellIdentifierList cid_list) modifies ts_BSSAP_BSSMAP := { pdu := { @@ -166,5 +187,55 @@ modifies ts_BSSAP_BSSMAP := { } } +const OCT1 ChRate_TCHF := '08'O; +const OCT1 ChRate_TCHH := '09'O; + +template (value) BSSMAP_IE_ChannelType ts_BSSMAP_IE_ChannelType := { + elementIdentifier := '0B'O, /* overwritten */ + lengthIndicator := 0, /* overwritten */ + speechOrDataIndicator := '0001'B, /* speech */ + spare1_4 := '0000'B, + channelRateAndType := ChRate_TCHF, + speechId_DataIndicator := '01'O /* FRv1 */ +} + +template (value) BSSMAP_IE_CircuitIdentityCode ts_BSSMAP_IE_CIC(uint11_t span, uint5_t ts) := { + elementIdentifier := '01'O, /* overwritten */ + cicHigh := bit2oct(substr(int2bit(span, 11) << 5, 0, 8)), + cicLow := bit2oct((substr(int2bit(span, 11), 8, 3) << 5) & int2bit(ts, 5)) +} + +template (value) PDU_BSSAP ts_BSSMAP_AssignmentCmd(uint11_t span, uint5_t ts) +modifies ts_BSSAP_BSSMAP := { + pdu := { + bssmap := { + assignmentRequest := { + messageType :='01'O, /* overwritten */ + channelType := ts_BSSMAP_IE_ChannelType, + layer3HeaderInfo := omit, + priority := omit, + circuitIdentityCode := ts_BSSMAP_IE_CIC(span, ts), + downLinkDTX_Flag := omit, + interferenceBandToBeUsed := omit, + classmarkInformationType2 := omit, + groupCallReference := omit, + talkerFlag := omit, + configurationEvolutionIndication := omit, + lsaAccesControlSuppression := omit, + serviceHandover := omit, + encryptionInformation := omit, + talkerPriority := omit, + aoIPTransportLayer := omit, + codecList := omit, + callIdentifier := omit, + kC128 := omit, + globalCallReference := omit, + lCLS_Configuration := omit, + lCLS_ConnectionStatusControl := omit, + lCLS_CorrelationNotNeeded := omit + } + } + } +} } diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn index 8f60b5c63..4c182f881 100644 --- a/library/L3_Templates.ttcn +++ b/library/L3_Templates.ttcn @@ -33,10 +33,10 @@ private function f_enc_IMSI_L3(hexstring digits) return IMSI_L3 { var IMSI_L3 l3; var integer len := lengthof(digits); if (len rem 2 == 1) { /* modulo remainder */ - l3.oddevenIndicator := '0'B; + l3.oddevenIndicator := '1'B; l3.fillerDigit := '1111'B; } else { - l3.oddevenIndicator := '1'B; + l3.oddevenIndicator := '0'B; l3.fillerDigit := omit; } l3.digits := digits;