| module MSC_Tests { |
| |
| import from General_Types all; |
| import from Osmocom_Types all; |
| |
| import from M3UA_Types all; |
| import from M3UA_Emulation all; |
| |
| import from MTP3asp_Types all; |
| import from MTP3asp_PortType all; |
| |
| import from SCCPasp_Types all; |
| import from SCCP_Types all; |
| import from SCCP_Emulation all; |
| |
| import from SCTPasp_Types all; |
| import from SCTPasp_PortType all; |
| |
| import from Osmocom_CTRL_Functions all; |
| import from Osmocom_CTRL_Types all; |
| import from Osmocom_CTRL_Adapter all; |
| |
| import from MNCC_Emulation all; |
| import from MNCC_Types all; |
| |
| import from GSUP_Emulation all; |
| import from GSUP_Types all; |
| import from IPA_Emulation all; |
| |
| import from BSSAP_Types all; |
| import from BSSAP_Adapter all; |
| import from BSSAP_CodecPort all; |
| import from BSSMAP_Templates all; |
| import from BSSMAP_Emulation all; |
| import from BSC_ConnectionHandler all; |
| |
| import from MobileL3_Types all; |
| import from MobileL3_CommonIE_Types all; |
| import from L3_Templates all; |
| |
| |
| type component MTC_CT extends BSSAP_Adapter_CT, CTRL_Adapter_CT { |
| var boolean g_initialized := false; |
| |
| /* no 'adapter_CT' for MNCC or GSUP */ |
| var MNCC_Emulation_CT vc_MNCC; |
| var GSUP_Emulation_CT vc_GSUP; |
| var IPA_Emulation_CT vc_GSUP_IPA; |
| |
| /* only to get events from IPA underneath GSUP */ |
| port IPA_CTRL_PT GSUP_IPA_EVENT; |
| } |
| |
| modulepar { |
| /* remote parameters of IUT */ |
| charstring mp_msc_ip := "127.0.0.1"; |
| integer mp_msc_ctrl_port := 4255; |
| integer mp_msc_vty_port := 4254; |
| |
| /* local parameters of emulated HLR */ |
| charstring mp_hlr_ip := "127.0.0.1"; |
| integer mp_hlr_port := 4222; |
| |
| charstring mp_msc_mncc := "/tmp/mncc"; |
| } |
| |
| |
| function f_init_mncc(charstring id) runs on MTC_CT { |
| id := id & "-MNCC"; |
| var MnccOps ops := { |
| create_cb := refers(MNCC_Emulation.ExpectedCreateCallback), |
| unitdata_cb := refers(MNCC_Emulation.DummyUnitdataCallback) |
| } |
| |
| vc_MNCC := MNCC_Emulation_CT.create(id); |
| map(vc_MNCC:MNCC, system:MNCC_CODEC_PT); |
| vc_MNCC.start(MNCC_Emulation.main(ops, id, mp_msc_mncc)); |
| } |
| |
| function f_init_gsup(charstring id) runs on MTC_CT { |
| id := id & "-GSUP"; |
| var GsupOps ops := { |
| create_cb := refers(GSUP_Emulation.ExpectedCreateCallback) |
| } |
| |
| vc_GSUP_IPA := IPA_Emulation_CT.create(id & "-IPA"); |
| vc_GSUP := GSUP_Emulation_CT.create(id); |
| |
| map(vc_GSUP_IPA:IPA_PORT, system:IPA_CODEC_PT); |
| connect(vc_GSUP:GSUP, vc_GSUP_IPA:IPA_GSUP_PORT); |
| /* we use this hack to get events like ASP_IPA_EVENT_UP */ |
| connect(vc_GSUP_IPA:IPA_CTRL_PORT, self:GSUP_IPA_EVENT); |
| |
| vc_GSUP.start(GSUP_Emulation.main(ops, id)); |
| vc_GSUP_IPA.start(IPA_Emulation.main_server(mp_hlr_ip, mp_hlr_port)); |
| |
| /* wait for incoming connection to GSUP port before proceeding */ |
| timer T := 10.0; |
| T.start; |
| alt { |
| [] GSUP_IPA_EVENT.receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_UP)) { } |
| [] T.timeout { |
| setverdict(inconc, "No connection to GSUP Port"); |
| self.stop |
| } |
| } |
| } |
| |
| function f_init() runs on MTC_CT { |
| |
| if (g_initialized == true) { |
| return; |
| } |
| g_initialized := true; |
| |
| f_bssap_init("MSC_Test", BSC_BssmapOps); |
| f_ipa_ctrl_start(mp_msc_ip, mp_msc_ctrl_port); |
| f_init_mncc("MSC_Test"); |
| f_init_gsup("MSC_Test"); |
| } |
| |
| template PDU_BSSAP ts_BSSAP_BSSMAP := { |
| discriminator := '0'B, |
| spare := '0000000'B, |
| dlci := omit, |
| lengthIndicator := 0, /* overwritten by codec */ |
| pdu := ? |
| } |
| |
| template PDU_BSSAP tr_BSSAP_BSSMAP := { |
| discriminator := '0'B, |
| spare := '0000000'B, |
| dlci := omit, |
| lengthIndicator := ?, |
| pdu := { |
| bssmap := ? |
| } |
| } |
| |
| |
| type integer BssmapCause; |
| |
| template (value) BSSMAP_IE_Cause ts_BSSMAP_IE_Cause(BssmapCause val) := { |
| elementIdentifier := '04'O, |
| lengthIndicator := 0, |
| causeValue := int2bit(val, 7), |
| extensionCauseValue := '0'B, |
| spare1 := omit |
| } |
| |
| template (value) PDU_BSSAP ts_BSSMAP_Reset(BssmapCause cause) modifies ts_BSSAP_BSSMAP := { |
| pdu := { |
| bssmap := { |
| reset := { |
| messageType := '30'O, |
| cause := ts_BSSMAP_IE_Cause(cause), |
| a_InterfaceSelectorForReset := omit |
| } |
| } |
| } |
| } |
| |
| template (value) PDU_BSSAP ts_BSSMAP_ResetAck modifies ts_BSSAP_BSSMAP := { |
| pdu := { |
| bssmap := { |
| resetAck := { |
| messageType := '31'O, |
| a_InterfaceSelectorForReset := omit |
| } |
| } |
| } |
| } |
| |
| template PDU_BSSAP tr_BSSMAP_ResetAck modifies tr_BSSAP_BSSMAP := { |
| pdu := { |
| bssmap := { |
| resetAck := { |
| messageType := '31'O, |
| a_InterfaceSelectorForReset := * |
| } |
| } |
| } |
| } |
| |
| template BSSMAP_IE_CellIdentifier ts_BSSMAP_IE_CellID := { |
| elementIdentifier := '05'O, |
| lengthIndicator := 0, |
| cellIdentifierDiscriminator := '0000'B, |
| spare1_4 := '0000'B, |
| cellIdentification := ? |
| } |
| |
| type uint16_t BssmapLAC; |
| type uint16_t BssmapCI; |
| |
| /* |
| template BSSMAP_IE_CellIdentifier ts_CellId_CGI(mcc, mnc, lac, ci) |
| modifies ts_BSSMAP_IE_CellID := { |
| cellIdentification := { |
| cI_LAC_CGI := { |
| mnc_mcc := FIXME, |
| lac := int2oct(lac, 2), |
| ci := int2oct(ci, 2) |
| } |
| } |
| } |
| */ |
| |
| template BSSMAP_IE_CellIdentifier ts_CellID_LAC_CI(BssmapLAC lac, BssmapCI ci) |
| modifies ts_BSSMAP_IE_CellID := { |
| cellIdentification := { |
| cI_LAC_CI := { |
| lac := int2oct(lac, 2), |
| ci := int2oct(ci, 2) |
| } |
| } |
| } |
| |
| template BSSMAP_IE_CellIdentifier ts_CellId_CI(BssmapCI ci) |
| modifies ts_BSSMAP_IE_CellID := { |
| cellIdentification := { |
| cI_CI := int2oct(ci, 2) |
| } |
| } |
| |
| template BSSMAP_IE_CellIdentifier ts_CellId_none |
| modifies ts_BSSMAP_IE_CellID := { |
| cellIdentification := { |
| cI_noCell := ''O |
| } |
| } |
| |
| |
| template BSSMAP_IE_Layer3Information ts_BSSMAP_IE_L3Info(octetstring l3info) := { |
| elementIdentifier := '17'O, |
| lengthIndicator := 0, |
| layer3info := l3info |
| } |
| |
| template PDU_BSSAP ts_BSSMAP_ComplL3(BSSMAP_IE_CellIdentifier cell_id, octetstring l3_info) |
| modifies ts_BSSAP_BSSMAP := { |
| pdu := { |
| bssmap := { |
| completeLayer3Information := { |
| messageType := '57'O, |
| cellIdentifier := cell_id, |
| layer3Information := ts_BSSMAP_IE_L3Info(l3_info), |
| chosenChannel := omit, |
| lSAIdentifier := omit, |
| aPDU := omit, |
| codecList := omit, |
| redirectAttemptFlag := omit, |
| sendSequenceNumber := omit, |
| iMSI := omit |
| } |
| } |
| } |
| } |
| |
| template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_IE_CellIdentifierList cid_list) |
| modifies ts_BSSAP_BSSMAP := { |
| pdu := { |
| bssmap := { |
| handoverRequired := { |
| messageType := '11'O, |
| cause := ts_BSSMAP_IE_Cause(cause), |
| responseRequest := omit, |
| cellIdentifierList := cid_list, |
| circuitPoolList := omit, |
| currentChannelType1 := omit, |
| speechVersion := omit, |
| queueingIndicator := omit, |
| oldToNewBSSInfo := omit, |
| sourceToTargetRNCTransparentInfo := omit, |
| sourceToTargetRNCTransparentInfoCDMA := omit, |
| gERANClassmark := omit, |
| talkerPriority := omit, |
| speechCodec := omit, |
| cSG_Identifier := omit |
| } |
| } |
| } |
| } |
| |
| // enc_PDU_BSSAP |
| |
| function f_send_BSSAP_UNITDATA(template PDU_BSSAP bssap) runs on MTC_CT { |
| BSSAP.send(ts_BSSAP_UNITDATA_req(g_sccp_addr_peer, g_sccp_addr_own, bssap)) |
| } |
| |
| type function void_fn(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr; |
| |
| function f_gen_imsi(hexstring prefix, integer suffix) return hexstring { |
| var integer suffix_len := 15 - lengthof(prefix); |
| suffix_len := suffix_len-1; /* FIXME: fix odd IMSI length */ |
| return prefix & int2hex(suffix, suffix_len); |
| } |
| |
| function f_gen_msisdn(hexstring prefix, integer suffix) return hexstring { |
| var integer suffix_len := 12 - lengthof(prefix); |
| return prefix & int2hex(suffix, suffix_len); |
| } |
| |
| /* FIXME: move into BSC_ConnectionHandler? */ |
| function f_start_handler(void_fn fn, charstring id, integer imsi_suffix) runs on MTC_CT return BSC_ConnHdlr { |
| var BSC_ConnHdlr vc_conn; |
| var BSC_ConnHdlrPars pars := { |
| sccp_addr_own := g_sccp_addr_own, |
| sccp_addr_peer := g_sccp_addr_peer, |
| cell_id := valueof(ts_CellId_CGI('262'H, '042'H, 23, 42)), |
| imsi := f_gen_imsi('26242'H, imsi_suffix), |
| msisdn := f_gen_msisdn('491239999'H, imsi_suffix), |
| cm2 := valueof(ts_CM2_default), |
| cm3 := omit |
| }; |
| |
| vc_conn := BSC_ConnHdlr.create(id); |
| /* BSSMAP part / A interface */ |
| connect(vc_conn:BSSAP, vc_BSSMAP:CLIENT); |
| connect(vc_conn:BSSAP_PROC, vc_BSSMAP:PROC); |
| /* MNCC part */ |
| connect(vc_conn:MNCC, vc_MNCC:MNCC_CLIENT); |
| connect(vc_conn:MNCC_PROC, vc_MNCC:MNCC_PROC); |
| /* GSUP part */ |
| connect(vc_conn:GSUP, vc_GSUP:GSUP_CLIENT); |
| connect(vc_conn:GSUP_PROC, vc_GSUP:GSUP_PROC); |
| |
| vc_conn.start(derefers(fn)(id, pars)); |
| return vc_conn; |
| } |
| |
| function f_sleep(float seconds) { |
| timer T := seconds; |
| T.start; |
| T.timeout; |
| } |
| |
| private function f_tc_lu_imsi_noauth_tmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| f_perform_lu(false, true, true); |
| } |
| testcase TC_lu_imsi_noauth_tmsi() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_imsi_noauth_tmsi), testcasename(), 1); |
| vc_conn.done; |
| } |
| |
| private function f_tc_lu_imsi_noauth_notmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| f_perform_lu(false, false, true); |
| } |
| testcase TC_lu_imsi_noauth_notmsi() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_imsi_noauth_notmsi), testcasename(), 2); |
| vc_conn.done; |
| } |
| |
| /* Do LU by IMSI, refuse it on GSUP and expect LU REJ back to MS */ |
| private function f_tc_lu_imsi_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi); |
| |
| f_create_gsup_expect(hex2str(g_pars.imsi)); |
| f_bssap_compl_l3(l3_lu); |
| GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); |
| GSUP.send(ts_GSUP_UL_ERR(g_pars.imsi, 23)); |
| alt { |
| [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej(int2oct(23,1)))) { } |
| [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) { |
| setverdict(fail, "Expecting LU REJ, but got ACCEPT"); |
| self.stop; |
| } |
| } |
| BSSAP.receive(tr_BSSMAP_ClearCommand); |
| BSSAP.send(ts_BSSMAP_ClearComplete); |
| BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); |
| setverdict(pass); |
| } |
| testcase TC_lu_imsi_reject() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_imsi_reject), testcasename(), 3); |
| vc_conn.done; |
| } |
| |
| /* Do LU by IMSI, timeout on GSUP */ |
| private function f_tc_lu_imsi_timeout_gsup(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi); |
| |
| f_create_gsup_expect(hex2str(g_pars.imsi)); |
| f_bssap_compl_l3(l3_lu); |
| GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); |
| /* Normally the HLR would need to respond here, but we decide to force a timeout here */ |
| alt { |
| /* FIXME: Expect specific reject cause */ |
| [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) { } |
| [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) { |
| setverdict(fail, "Expecting LU REJ, but got ACCEPT"); |
| self.stop; |
| } |
| } |
| BSSAP.receive(tr_BSSMAP_ClearCommand); |
| BSSAP.send(ts_BSSMAP_ClearComplete); |
| BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); |
| setverdict(pass); |
| } |
| testcase TC_lu_imsi_timeout_gsup() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_imsi_timeout_gsup), testcasename(), 4); |
| vc_conn.done; |
| } |
| |
| private function f_tc_lu_imsi_auth_tmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| f_perform_lu(true, true, true); |
| } |
| testcase TC_lu_imsi_auth_tmsi() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_imsi_auth_tmsi), testcasename(), 5); |
| vc_conn.done; |
| } |
| |
| |
| /* Send CM SERVICE REQ for IMSI that has never performed LU before */ |
| private function f_tc_cmserv_imsi_unknown(charstring id, BSC_ConnHdlrPars pars) |
| runs on BSC_ConnHdlr { |
| |
| var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); |
| var BSSMAP_IE_CellIdentifier cell_id := valueof(ts_CellId_CGI('262'H, '042'H, 23, 42)); |
| var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ('0001'B, mi)); |
| |
| f_create_gsup_expect(hex2str(g_pars.imsi)); |
| |
| /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ |
| f_bssap_compl_l3(l3_info); |
| |
| timer T := 10.0; |
| T.start; |
| alt { |
| [] BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ)) { } |
| //[] BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC)) { } |
| [] BSSAP.receive { setverdict(fail, "Received unexpected BSSAP"); } |
| [] GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)) { |
| setverdict(fail, "Unexpected GSUP UL REQ"); |
| } |
| [] T.timeout { setverdict(inconc, "Timeout waiting for CM SERV REQ"); } |
| } |
| |
| alt { |
| [] BSSAP.receive(tr_BSSMAP_ClearCommand) { |
| setverdict(pass); |
| } |
| [] BSSAP.receive { setverdict(fail, "Received unexpected BSSAP"); } |
| [] T.timeout { setverdict(inconc, "Timeout waiting for CM SERV REQ"); } |
| } |
| } |
| testcase TC_cmserv_imsi_unknown() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| vc_conn := f_start_handler(refers(f_tc_cmserv_imsi_unknown), testcasename(), 5); |
| vc_conn.done; |
| } |
| |
| private function f_tc_lu_and_mo_call(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { |
| g_pars := pars; |
| f_perform_lu(false, true, true); |
| |
| f_establish_fully(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false); |
| |
| var hexstring called := '12345'H; |
| var integer tid := 0; |
| var MNCC_PDU mncc; |
| f_create_mncc_expect(hex2str(called)); |
| |
| BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(tid, called))); |
| MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(called)))) -> value mncc; |
| /* FIXME: extract call_id */ |
| |
| /* Call Proceeding */ |
| MNCC.send(ts_MNCC_CALL_PROC_req(mncc.u.signal.callref, ts_MNCC_bcap_voice)); |
| BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(tid))); |
| |
| /* Alerting */ |
| MNCC.send(ts_MNCC_ALERT_req(mncc.u.signal.callref)); |
| BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_ALERTING(tid))); |
| |
| /* Answer. This causes TCH assignment in case of "late assignment" */ |
| MNCC.send(ts_MNCC_SETUP_COMPL_req(mncc.u.signal.callref)); |
| |
| f_sleep(3.0); |
| |
| /* Hangup by "B" side */ |
| MNCC.send(ts_MNCC_DISC_req(mncc.u.signal.callref, valueof(ts_MNCC_cause(23)))); |
| BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(tid))); |
| |
| /* Release of call */ |
| MNCC.send(ts_MNCC_REL_req(mncc.u.signal.callref, valueof(ts_MNCC_cause(42)))); |
| BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(tid))); |
| |
| /* clearing of radio channel */ |
| BSSAP.receive(tr_BSSMAP_ClearCommand); |
| BSSAP.send(ts_BSSMAP_ClearComplete); |
| BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); |
| |
| f_sleep(5.0); |
| } |
| testcase TC_lu_and_mo_call() runs on MTC_CT { |
| var BSC_ConnHdlr vc_conn; |
| f_init(); |
| |
| vc_conn := f_start_handler(refers(f_tc_lu_and_mo_call), testcasename(), 1); |
| vc_conn.done; |
| } |
| |
| |
| |
| control { |
| execute( TC_cmserv_imsi_unknown() ); |
| execute( TC_lu_imsi_noauth_tmsi() ); |
| //execute( TC_lu_imsi_noauth_notmsi() ); |
| execute( TC_lu_imsi_reject() ); |
| execute( TC_lu_imsi_timeout_gsup() ); |
| execute( TC_lu_and_mo_call() ); |
| } |
| |
| |
| } |