| module MSC_ConnectionHandler { |
| |
| import from General_Types all; |
| import from Osmocom_Types all; |
| import from GSM_Types all; |
| import from SCCPasp_Types all; |
| import from BSSAP_Types all; |
| import from BSSMAP_Emulation all; |
| import from BSSMAP_Templates all; |
| |
| import from MGCP_Types all; |
| import from MGCP_Templates all; |
| import from SDP_Types all; |
| |
| import from RSL_Emulation all; |
| import from RSL_Types all; |
| |
| import from MobileL3_Types all; |
| import from MobileL3_CommonIE_Types all; |
| //import from MobileL3_RRM_Types all; |
| import from L3_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, RSL_DchanHdlr { |
| /* SCCP Connecction Identifier for the underlying SCCP connection */ |
| var integer g_sccp_conn_id; |
| |
| /* procedure port back to our parent (BSSMAP_Emulation_CT) for control */ |
| port BSSMAPEM_PROC_PT BSSMAPEM; |
| |
| var MSC_State g_state := MSC_STATE_NONE; |
| } |
| |
| /* 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; |
| |
| /* answer all RESET with a RESET ACK */ |
| if (match(bssap, tr_BSSMAP_Reset)) { |
| resp := ts_BSSMAP_ResetAck; |
| } |
| |
| return resp; |
| } |
| |
| const BssmapOps MSC_BssmapOps := { |
| create_cb := refers(BSSMAP_Emulation.ExpectedCreateCallback), |
| unitdata_cb := refers(UnitdataCallback), |
| decode_dtap := false, |
| role_ms := false |
| } |
| |
| 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 |
| } |
| |
| /* register an expect with the BSSMAP core */ |
| private function f_create_exp(octetstring l3_enc) runs on MSC_ConnHdlr { |
| BSSMAPEM.call(BSSMAPEM_register:{l3_enc, self}) { |
| [] BSSMAPEM.getreply(BSSMAPEM_register:{?, ?}) {}; |
| } |
| } |
| |
| type record TestHdlrParams { |
| OCT1 ra, |
| GsmFrameNumber fn, |
| hexstring imsi, |
| RslLinkId link_id |
| }; |
| |
| template (value) TestHdlrParams t_def_TestHdlrPars := { |
| ra := '23'O, |
| fn := 23, |
| imsi := '001019876543210'H, |
| link_id := valueof(ts_RslLinkID_DCCH(0)) |
| } |
| |
| function f_create_chan_and_exp(TestHdlrParams pars) runs on MSC_ConnHdlr { |
| var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(pars.imsi)); |
| var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); |
| var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3_info); |
| |
| /* call helper function for CHAN_RQD -> IMM ASS ->EST_IND */ |
| RSL_Emulation.f_chan_est(pars.ra, l3_enc, pars.link_id, pars.fn); |
| f_create_exp(l3_enc); |
| } |
| |
| function f_rsl_reply(template PDU_ML3_MS_NW l3, RSL_Message orig) runs on MSC_ConnHdlr { |
| var RslChannelNr chan_nr := orig.ies[0].body.chan_nr; |
| var RslLinkId link_id; |
| if (orig.msg_type == RSL_MT_ENCR_CMD) { |
| link_id := orig.ies[2].body.link_id; |
| } else { |
| link_id := orig.ies[1].body.link_id; |
| } |
| RSL.send(ts_RSL_DATA_IND(chan_nr, link_id, enc_PDU_ML3_MS_NW(valueof(l3)))); |
| } |
| |
| function f_cipher_mode(OCT1 alg, OCT8 key, template OCT16 kc128 := omit, boolean exp_fail := false) |
| runs on MSC_ConnHdlr { |
| var PDU_BSSAP bssap; |
| var RSL_Message rsl; |
| |
| if (isvalue(kc128)) { |
| BSSAP.send(ts_BSSMAP_CipherModeCmdKc128(alg, key, valueof(kc128))); |
| } else { |
| BSSAP.send(ts_BSSMAP_CipherModeCmd(alg, key)); |
| } |
| alt { |
| /* RSL/UE Side */ |
| [] RSL.receive(tr_RSL_ENCR_CMD(g_chan_nr, ?, alg, key)) -> value rsl { |
| var PDU_ML3_NW_MS l3 := dec_PDU_ML3_NW_MS(rsl.ies[3].body.l3_info.payload); |
| log("Rx L3 from net: ", l3); |
| if (ischosen(l3.msgs.rrm.cipheringModeCommand)) { |
| f_rsl_reply(ts_RRM_CiphModeCompl, rsl); |
| } |
| repeat; |
| } |
| [] BSSAP.receive(tr_BSSMAP_CipherModeCompl) -> value bssap { |
| // bssap.bssmap.cipherModeComplete.chosenEncryptionAlgorithm.algoritmhIdentifier |
| if (exp_fail == true) { |
| setverdict(fail, "Unexpected Cipher Mode Complete"); |
| } else { |
| setverdict(pass); |
| } |
| } |
| [] BSSAP.receive(tr_BSSMAP_CipherModeRej) -> value bssap { |
| if (exp_fail == false) { |
| setverdict(fail, "Ciphering Mode Reject"); |
| } else { |
| setverdict(pass); |
| } |
| } |
| } |
| } |
| |
| /* establish a channel fully, expecting an assignment matching 'exp' */ |
| function f_establish_fully(TestHdlrParams pars, PDU_BSSAP ass_cmd, template PDU_BSSAP exp_ass_cpl) |
| runs on MSC_ConnHdlr return PDU_BSSAP { |
| var PDU_BSSAP bssap; |
| var RSL_Message rsl; |
| timer T := 10.0; |
| var boolean exp_compl := ischosen(exp_ass_cpl.pdu.bssmap.assignmentComplete); |
| var boolean crcx_seen := false; |
| var boolean rr_modify_seen := false; |
| |
| f_create_chan_and_exp(pars); |
| /* we should now have a COMPL_L3 at the MSC */ |
| |
| BSSAP.receive(tr_BSSMAP_ComplL3); |
| BSSAP.send(ass_cmd); |
| alt { |
| /* if we receive exactly what we expected, always return + pass */ |
| [] BSSAP.receive(exp_ass_cpl) -> value bssap { |
| setverdict(pass); |
| return bssap; |
| } |
| [rr_modify_seen == false] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr)) -> value rsl { |
| var PDU_ML3_NW_MS l3 := dec_PDU_ML3_NW_MS(rsl.ies[2].body.l3_info.payload); |
| log("Rx L3 from net: ", l3); |
| if (ischosen(l3.msgs.rrm.channelModeModify)) { |
| f_rsl_reply(ts_RRM_ModeModifyAck(l3.msgs.rrm.channelModeModify.channelDescription, |
| l3.msgs.rrm.channelModeModify.channelMode), rsl); |
| rr_modify_seen := true; |
| } |
| repeat; |
| } |
| [rr_modify_seen] RSL.receive(tr_RSL_MsgTypeD(RSL_MT_MODE_MODIFY_REQ)) -> value rsl { |
| RSL.send(ts_RSL_MODE_MODIFY_ACK(g_chan_nr)); |
| repeat; |
| } |
| [crcx_seen == false] RSL.receive(tr_RSL_IPA_CRCX(g_chan_nr)) -> value rsl { |
| RSL.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, 1, 1, 1, 1)); |
| crcx_seen := true; |
| repeat; |
| } |
| [crcx_seen] RSL.receive(tr_RSL_IPA_MDCX(g_chan_nr, ?)) -> value rsl{ |
| RSL.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, 1, 1, 1, 1)); |
| repeat; |
| } |
| [exp_compl] BSSAP.receive(tr_BSSMAP_AssignmentComplete) { |
| setverdict(fail, "Received non-matching ASSIGNMENT COMPLETE"); |
| } |
| [exp_compl] BSSAP.receive(tr_BSSMAP_AssignmentFail) { |
| setverdict(fail, "Received unexpected ASSIGNMENT FAIL"); |
| } |
| [not exp_compl] BSSAP.receive(tr_BSSMAP_AssignmentComplete) { |
| setverdict(fail, "Received unexpected ASSIGNMENT COMPLETE"); |
| } |
| [not exp_compl] BSSAP.receive(tr_BSSMAP_AssignmentFail) { |
| setverdict(fail, "Received non-matching ASSIGNMENT FAIL"); |
| } |
| [] T.timeout { |
| setverdict(inconc, "Timeout waiting for ASSIGNMENT COMPLETE"); |
| } |
| } |
| |
| self.stop; |
| } |
| |
| |
| } |