From 3c68a548962ae8cf1a20304f18b735f2e29aae39 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 27 Jan 2018 16:03:20 +0100 Subject: msc: rename sub-directory from msc_tests to msc (to be in line with bsc, etc.) Change-Id: Ib219dee5e587bca3b79d29d631836cf1ef2be81c --- msc/BSC_ConnectionHandler.ttcn | 497 ++++++++++++++ msc/MSC_Tests.cfg | 42 ++ msc/MSC_Tests.ttcn | 1479 ++++++++++++++++++++++++++++++++++++++++ msc/gen_links.sh | 84 +++ msc/regen_makefile.sh | 5 + 5 files changed, 2107 insertions(+) create mode 100644 msc/BSC_ConnectionHandler.ttcn create mode 100644 msc/MSC_Tests.cfg create mode 100644 msc/MSC_Tests.ttcn create mode 100755 msc/gen_links.sh create mode 100755 msc/regen_makefile.sh (limited to 'msc') diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn new file mode 100644 index 00000000..a7ce1eae --- /dev/null +++ b/msc/BSC_ConnectionHandler.ttcn @@ -0,0 +1,497 @@ +module BSC_ConnectionHandler { + +import from General_Types all; +import from Osmocom_Types all; +import from Native_Functions all; +import from GSM_Types all; +import from IPL4asp_Types all; +import from SCCPasp_Types all; +import from BSSAP_Types all; +import from BSSMAP_Emulation all; +import from BSSMAP_Templates all; + +import from GSUP_Types all; +import from GSUP_Emulation all; + +import from MNCC_Types all; +import from MNCC_Emulation all; + +import from MGCP_Types all; +import from MGCP_Emulation all; +import from MGCP_Templates all; +import from SDP_Types all; + +import from MobileL3_Types all; +import from MobileL3_CommonIE_Types all; +import from MobileL3_MM_Types all; +import from MobileL3_CC_Types all; +import from L3_Templates all; + +/* this component represents a single subscriber connection */ +type component BSC_ConnHdlr extends BSSAP_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, MGCP_ConnHdlr { + var BSC_ConnHdlrPars g_pars; + timer g_Tguard := 60.0; +} + +type record AuthVector { + OCT16 rand, + OCT4 sres, + OCT8 kc + /* FIXME: 3G elements */ +} + +type record BSC_ConnHdlrPars { + SCCP_PAR_Address sccp_addr_own, + SCCP_PAR_Address sccp_addr_peer, + BSSMAP_IE_CellIdentifier cell_id, + hexstring imei, + hexstring imsi, + hexstring msisdn, + OCT4 tmsi optional, + BSSMAP_IE_ClassmarkInformationType2 cm2, + BSSMAP_IE_ClassmarkInformationType3 cm3 optional, + AuthVector vec optional +}; + +/* altstep for the global guard timer */ +private altstep as_Tguard() runs on BSC_ConnHdlr { + [] g_Tguard.timeout { + setverdict(inconc, "Tguard timeout"); + self.stop; + } +} + +/* init function, called as first function in new BSC_ConnHdlr */ +function f_init_handler(BSC_ConnHdlrPars pars, float t_guard := 60.0) runs on BSC_ConnHdlr { + /* make parameters available via component variable */ + g_pars := pars; + /* Start guard timer and activate it as default */ + g_Tguard.start(t_guard); + activate(as_Tguard()); +} + + +/* Callback function from general BSSMAP_Emulation whenever a connectionless + * BSSMAP message arrives. Canreturn a PDU_BSSAPthat should be sent in return */ +private function BscUnitdataCallback(PDU_BSSAP bssap) +runs on BSSMAP_Emulation_CT return template PDU_BSSAP { + var template PDU_BSSAP resp := omit; + + log("BSSMAP_BscUnitdataCallback"); + /* answer all RESET with RESET ACK */ + if (match(bssap, tr_BSSMAP_Reset)){ + log("BSSMAP_BscUnitdataCallback: Responding to RESET with RESET-ACK"); + resp := ts_BSSMAP_ResetAck; + } + + /* FIXME: Handle paging, etc. */ + return resp; +} + +const BssmapOps BSC_BssmapOps := { + /* Create call-back for inbound connections from MSC (hand-over) */ + create_cb := refers(BSSMAP_Emulation.ExpectedCreateCallback), + unitdata_cb := refers(BscUnitdataCallback), + decode_dtap := true, + role_ms := true +} + + +private function MnccUnitdataCallback(MNCC_PDU mncc) +runs on MNCC_Emulation_CT return template MNCC_PDU { + log("Ignoring MNCC", mncc); + return omit; +} + +const MnccOps BCC_MnccOps := { + create_cb := refers(MNCC_Emulation.ExpectedCreateCallback), + unitdata_cb := refers(MnccUnitdataCallback) +} + + + +template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := { + addr_peer := peer, + addr_own := own, + bssap := bssap +}; + +/* Encode 'l3' and ask BSSMAP_Emulation to create new connection with COMPL L3 INFO */ +function f_bssap_compl_l3(PDU_ML3_MS_NW l3) +runs on BSC_ConnHdlr { + log("Sending COMPL L3: ", l3); + var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3); + BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own, + valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, l3_enc)))); + alt { + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_CONF_IND) {} + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) { + setverdict(fail, "DISC.ind from SCCP"); + self.stop; + } + } +} + +/* helper function to fully establish a dedicated channel */ +function f_establish_fully(MobileIdentityLV mi, boolean expect_auth, boolean expect_ciph) +runs on BSC_ConnHdlr { + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); + var PDU_DTAP_MT dtap_mt; + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_info); + + f_mm_common(expect_auth, expect_ciph); + if (expect_ciph) { + /* implicit CM SERVICE ACCEPT? */ + } else { + /* explicit CM SERVICE ACCEPT */ + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC)); + } +} + +/* helper function to fully establish a dedicated channel */ +function f_establish_fully_pag(MobileIdentityLV mi, boolean expect_auth, boolean expect_ciph) +runs on BSC_ConnHdlr { + var PDU_ML3_MS_NW l3_info := valueof(ts_PAG_RESP(mi)); + var PDU_DTAP_MT dtap_mt; + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_info); + + f_mm_common(expect_auth, expect_ciph); +} + + +/* build a PDU_ML3_MS_NW containing a Location Update by IMSI */ +function f_build_lu_imsi(hexstring imsi) return PDU_ML3_MS_NW +{ + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(imsi)); + return f_build_lu(mi); +} +function f_build_lu_imei(hexstring imei) return PDU_ML3_MS_NW +{ + var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(imei)); + return f_build_lu(mi); +} +function f_build_lu_tmsi(OCT4 tmsi) return PDU_ML3_MS_NW +{ + var MobileIdentityLV mi := valueof(ts_MI_TMSI_LV(tmsi)); + return f_build_lu(mi); +} +private function f_build_lu(MobileIdentityLV mi) return PDU_ML3_MS_NW +{ + var LocationAreaIdentification_V old_lai := { '62F220'O, '9999'O }; + var PDU_ML3_MS_NW l3_info := valueof(ts_ML3_MO_LU_Req(valueof(ts_ML3_IE_LuType_Attach), + old_lai, mi, valueof(ts_CM1))); + return l3_info; +} + +private function f_rnd_oct(integer len) return octetstring { + var integer i; + var octetstring res; + for (i := 0; i < len; i := i + 1) { + res[i] := int2oct(float2int(rnd()*256.0), 1); + } + return res; +} + +function f_gen_auth_vec_2g() return AuthVector { + var AuthVector vec; + vec.rand := f_rnd_oct(16); + vec.sres := f_rnd_oct(4); + vec.kc := f_rnd_oct(8); + return vec; +} + + +function f_mm_common(boolean expect_auth, boolean expect_ciph) runs on BSC_ConnHdlr +{ + if (expect_auth) { + g_pars.vec := f_gen_auth_vec_2g(); + var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand, + g_pars.vec.sres, + g_pars.vec.kc)); + GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple)); + + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ(g_pars.vec.rand))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_2G(g_pars.vec.sres))); + } + + if (expect_ciph) { + BSSAP.receive(tr_BSSMAP_CipherModeCmd(?, g_pars.vec.kc)); + BSSAP.send(ts_BSSMAP_CipherModeCompl('02'O)); + } +} + +function f_perform_lu(boolean expect_auth, boolean expect_tmsi, boolean send_early_cm, + boolean expect_ciph := false) +runs on BSC_ConnHdlr { + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + if (send_early_cm) { + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + } + + f_mm_common(expect_auth, expect_ciph); + + /* Expect MSC to perform LU with HLR */ + GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn)); + GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi)); + GSUP.send(ts_GSUP_UL_RES(g_pars.imsi)); + + alt { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt { + var PDU_ML3_LocationUpdateAccept lu_acc := dtap_mt.dtap.msgs.mm.locationUpdateAccept; + if (expect_tmsi) { + if (not ispresent(lu_acc.mobileIdentityTLV) or + not ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) { + setverdict(fail, "Expected TMSI but no TMSI was allocated"); + self.stop; + } else { + g_pars.tmsi := lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets; + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_TmsiRealloc_Cmpl)); + } + } else { + if (ispresent(lu_acc.mobileIdentityTLV) and + ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) { + setverdict(fail, "Expected no TMSI but TMSI was allocated"); + self.stop; + } + } + } + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) { + setverdict(fail, "Expected LU ACK, but received LU REJ"); + self.stop; + } + } + /* FIXME: there could be pending SMS or other common procedures by the MSC, let's ignore them */ + BSSAP.receive(tr_BSSMAP_ClearCommand); + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + setverdict(pass); +} + +function f_foo() runs on BSC_ConnHdlr{ + /* SCCP CC handled by BSSMAP_Emulation_CT.main() */ + /* Expect auth, if enabled */ + + /* TODO: ISD */ + /* Expect encr, if enabled */ + /* Expect encr, if enabled */ + /* Expect ASS CMD, if chan_type != requested */ + /* Send ASS CMPL in successful case */ + + /* Expect AoIP port/ip information for RTP stream */ + /* Expect MSC-originated MGCP to our simulated MGW */ + /* Verify Counters via CTRL */ + /* re-configure MSC behaviour via VTY */ +} + +/* parameters related to a (MO?) voice call */ +type record CallParameters { + boolean expect_auth, /* do we expect AUTHENTICATE from network */ + boolean expect_ciph, /* do we expect CIPHER MODE from network */ + + /* CC related parameters */ + hexstring called_party, /* whom are we calling */ + integer transaction_id optional, /* which TS 04.08 CC transaction ID to use */ + BearerCapability_TLV bearer_cap, /* which bearer capabilities to claim */ + + /* MNCC related parameters */ + uint32_t mncc_callref optional, /* call reference on the MNCC side */ + MNCC_bearer_cap mncc_bearer_cap optional, /* MNCC-side bearer capabilities */ + + /* RTP related parameters */ + HostName bss_rtp_ip optional, /* BSS Side RTP IP */ + PortNumber bss_rtp_port optional, /* BSS Side RTP Port */ + HostName mss_rtp_ip optional, /* MSS Side RTP IP */ + PortNumber mss_rtp_port optional, /* MSS Side RTP Port */ + HostName mgw_rtp_ip_bss, /* BSS-facing MGW RTP IP */ + PortNumber mgw_rtp_port_bss, /* BSS-facing MGW RTP Port */ + HostName mgw_rtp_ip_mss, /* MSS-facing MGW RTP IP */ + PortNumber mgw_rtp_port_mss, /* MSS-facing MGW RTP Port */ + uint7_t rtp_payload_type, /* dynamic RTP payload type */ + charstring rtp_sdp_format, /* AMR/8000 or the like */ + + MgcpCallId mgcp_call_id optional, /* MGCP Call ID; CallAgent allocated */ + MgcpEndpoint mgcp_ep optional /* MGCP Endpoint, CallAgent or MGW allocated */, + MgcpConnectionId mgcp_connection_id_bss, /* MGCP Connection ID BSS Side */ + MgcpConnectionId mgcp_connection_id_mss /* MGCP Connection ID MSS Side */ +} + +template (value) CallParameters t_CallParams(hexstring called, integer tid) := { + expect_auth := false, + expect_ciph := false, + called_party := called, + transaction_id := tid, + bearer_cap := valueof(ts_Bcap_voice), + mncc_callref := omit, + mncc_bearer_cap := valueof(ts_MNCC_bcap_voice), + bss_rtp_ip := "9.8.7.6", + bss_rtp_port := 9000, + mss_rtp_ip := omit, + mss_rtp_port := omit, + mgw_rtp_ip_bss := "1.1.1.1", + mgw_rtp_port_bss := 10000, + mgw_rtp_ip_mss := "1.1.1.1", + mgw_rtp_port_mss := 11000, + rtp_payload_type := 98, + rtp_sdp_format := "AMR/8000", + mgcp_call_id := omit, + mgcp_ep := omit, + mgcp_connection_id_bss := '0'H,// + mgcp_connection_id_mss := '0'H // +}; + + +function f_mo_call(inout CallParameters cpars) +runs on BSC_ConnHdlr { + + var MobileIdentityLV mi; + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + + /* If we have a TMSI, use TMSI instead of IMSI */ + if (ispresent(g_pars.tmsi)) { + mi := valueof(ts_MI_TMSI_LV(g_pars.tmsi)); + } else { + mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + } + f_establish_fully(mi, cpars.expect_auth, cpars.expect_ciph); + + /* Create MNCC and MGCP expect */ + f_create_mncc_expect(hex2str(cpars.called_party)); + f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); + + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party))); + interleave { + [] MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc { + cpars.mncc_callref := mncc.u.signal.callref; + /* Call Proceeding */ + MNCC.send(ts_MNCC_CALL_PROC_req(cpars.mncc_callref, cpars.mncc_bearer_cap)); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(cpars.transaction_id))); + }; + /* First MGCP CRCX (for BSS/RAN side) */ + [] MGCP.receive(tr_CRCX) -> value mgcp_cmd { + cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd); + /* TODO: dynamic EP allocation case */ + cpars.mgcp_ep := mgcp_cmd.line.ep; + var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_bss, cpars.mgw_rtp_ip_bss, + hex2str(cpars.mgcp_call_id), "42", + cpars.mgw_rtp_port_bss, + { int2str(cpars.rtp_payload_type) }, + { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type, + cpars.rtp_sdp_format)), + valueof(ts_SDP_ptime(20)) })); + MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_bss, sdp)); + } + } + /* Second MGCP CRCX (this time for MSS/CN side) */ + MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd { + var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_mss, cpars.mgw_rtp_ip_mss, + hex2str(cpars.mgcp_call_id), "42", + cpars.mgw_rtp_port_mss, + { int2str(cpars.rtp_payload_type) }, + { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type, + cpars.rtp_sdp_format)), + valueof(ts_SDP_ptime(20)) })); + MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_mss, sdp)); + } + + /* Alerting */ + MNCC.send(ts_MNCC_ALERT_req(cpars.mncc_callref)); + + var BSSMAP_IE_AoIP_TransportLayerAddress tla_ass := + valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.mgw_rtp_ip_bss),cpars.mgw_rtp_port_bss)); + interleave { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_ALERTING(cpars.transaction_id))) {} + /* expect AoIP IP/Port to match what we returned in CRCX_ACK above */ + [] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)) { + var BSSMAP_IE_AoIP_TransportLayerAddress tla; + tla := valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.bss_rtp_ip), cpars.bss_rtp_port)); + BSSAP.send(ts_BSSMAP_AssignmentComplete(omit, tla)); + } + } + + /* Answer. MNCC_SETUP_RSP -> CONNECT to MS; CONNECT_ACK from MS */ + MNCC.send(ts_MNCC_SETUP_rsp(cpars.mncc_callref)); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CONNECT(cpars.transaction_id))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT_ACK(cpars.transaction_id))); + + f_sleep(3.0); + + /* Hangup by "B" side */ + MNCC.send(ts_MNCC_DISC_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23)))); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(cpars.transaction_id))); + + /* Release of call */ + MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(42)))); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id))); + + /* clearing of radio channel */ + interleave { + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + } + [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd { + /* TODO: For one or all connections on EP? */ + MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id)); + f_create_mgcp_delete_ep(cpars.mgcp_ep); + } + } + setverdict(pass); +} + +/* expect a clear command */ +function f_expect_clear(float t := 5.0) runs on BSC_ConnHdlr { + timer T := t; + + T.start; + alt { + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { } + [] BSSAP.receive { + setverdict(fail, "Unexpected BSSMAP while waiting for ClearCommand"); + self.stop; + } + [] T.timeout { + setverdict(inconc, "Timeout waiting for ClearCommand"); + self.stop; + } + } + + BSSAP.send(ts_BSSMAP_ClearComplete); + + T.start; + alt { + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) { + setverdict(pass); + } + [] BSSAP.receive { + setverdict(fail, "Unexpected BSSMAP while waiting for SCCP Release"); + self.stop; + } + [] T.timeout { + setverdict(inconc, "Timeout waiting for SCCP Release"); + self.stop; + } + } +} + + + + +} + + diff --git a/msc/MSC_Tests.cfg b/msc/MSC_Tests.cfg new file mode 100644 index 00000000..ab8a2ff0 --- /dev/null +++ b/msc/MSC_Tests.cfg @@ -0,0 +1,42 @@ +[LOGGING] +SourceInfoFormat := Single; +#FileMask := LOG_ALL | TTCN_DEBUG | TTCN_MATCHING | DEBUG_ENCDEC; +#ConsoleMask := ERROR | WARNING | TESTCASE | TTCN_MATCHING | DEBUG_ENCDEC +FileMask := LOG_ALL | TTCN_MATCHING; + +BSSAP.FileMask := LOG_NOTHING; +"MSC_Test-M3UA".FileMask := ERROR | WARNING; +"MSC_Test-SCCP".FileMask := ERROR | WARNING; +"MSC_Test-GSUP-IPA".FileMask := ERROR | WARNING; +"MSC_Test-GSUP".FileMask := ERROR | WARNING; +"IPA-CTRL-IPA".FileMask := ERROR | WARNING; +mtc.FileMask := ERROR | WARNING; + +[TESTPORT_PARAMETERS] +#*.*.udpReuseAddress := "yes"; +"MSC_Test-MNCC".MNCC.socket_type := "SEQPACKET"; +*.MSCVTY.CTRL_MODE := "client" +*.MSCVTY.CTRL_HOSTNAME := "127.0.0.1" +*.MSCVTY.CTRL_PORTNUM := "4254" +*.MSCVTY.CTRL_LOGIN_SKIPPED := "yes" +*.MSCVTY.CTRL_DETECT_SERVER_DISCONNECTED := "yes" +*.MSCVTY.CTRL_READMODE := "buffered" +*.MSCVTY.CTRL_CLIENT_CLEANUP_LINEFEED := "yes" +*.MSCVTY.PROMPT1 := "OsmoMSC> " + + +[MODULE_PARAMETERS] +M3UA_Emulation.tsp_logVerbose := true; +BSSAP_Adapter.mp_own_pc := 193; /* 0.23.3 */ +BSSAP_Adapter.mp_peer_pc := 185; /* 0.23.1 */ +BSSAP_Adapter.mp_sctp_addr := { 23906, "127.0.0.1", 2905, "127.0.0.1" }; +Osmocom_VTY_Functions.mp_prompt_prefix := "OsmoMSC"; + +[MAIN_CONTROLLER] + +[EXECUTE] +#MSC_Tests.TC_reset +#MSC_Tests.TC_cmserv_imsi_unknown +#MSC_Tests.TC_lu_imsi_noauth_tmsi +#MSC_Tests.TC_lu_imsi_noauth_notmsi +MSC_Tests.TC_lu_imsi_reject diff --git a/msc/MSC_Tests.ttcn b/msc/MSC_Tests.ttcn new file mode 100644 index 00000000..d0000d84 --- /dev/null +++ b/msc/MSC_Tests.ttcn @@ -0,0 +1,1479 @@ +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 TELNETasp_PortType all; +import from Osmocom_VTY_Functions all; + +import from MNCC_Emulation all; +import from MNCC_Types all; + +import from MGCP_Emulation all; +import from MGCP_Types all; +import from MGCP_Templates all; +import from SDP_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 MGCP_Emulation_CT vc_MGCP; + 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; + /* VTY to MSC */ + port TELNETasp_PT MSCVTY; +} + +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_mgcp(charstring id) runs on MTC_CT { + id := id & "-MGCP"; + var MGCPOps ops := { + create_cb := refers(MGCP_Emulation.ExpectedCreateCallback), + unitdata_cb := refers(MGCP_Emulation.DummyUnitdataCallback) + } + var MGCP_conn_parameters pars := { + callagent_ip := "127.0.0.1", + callagent_udp_port := -1, + mgw_ip := "127.0.0.1", + mgw_udp_port := 2427 + } + + vc_MGCP := MGCP_Emulation_CT.create(id); + map(vc_MGCP:MGCP, system:MGCP_CODEC_PT); + vc_MGCP.start(MGCP_Emulation.main(ops, pars, id)); +} + +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_mgcp("MSC_Test"); + f_init_gsup("MSC_Test"); + + map(self:MSCVTY, system:MSCVTY); + f_vty_set_prompts(MSCVTY); + f_vty_transceive(MSCVTY, "enable"); + + /* set some defaults */ + f_vty_config(MSCVTY, "network", "authentication optional"); + f_vty_config(MSCVTY, "msc", "assign-tmsi"); + f_vty_config(MSCVTY, "network", "encryption a5 0"); +} + +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; + +private function f_concat_pad(integer tot_len, hexstring prefix, integer suffix) return hexstring { + var integer suffix_len := tot_len - lengthof(prefix); + var charstring suffix_ch := int2str(suffix); + var integer pad_len := suffix_len - lengthof(suffix_ch); + + return prefix & int2hex(0, pad_len) & str2hex(suffix_ch); +} + +function f_gen_imei(integer suffix) return hexstring { + return f_concat_pad(15, '49999'H, suffix); +} + +function f_gen_imsi(integer suffix) return hexstring { + return f_concat_pad(15, '26242'H, suffix); +} + +function f_gen_msisdn(integer suffix) return hexstring { + return f_concat_pad(12, '49123'H, suffix); +} + +/* 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)), + imei := f_gen_imei(imsi_suffix), + imsi := f_gen_imsi(imsi_suffix), + msisdn := f_gen_msisdn(imsi_suffix), + tmsi := omit, + cm2 := valueof(ts_CM2_default), + cm3 := omit, + vec := 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); + /* MGCP part */ + connect(vc_conn:MGCP, vc_MGCP:MGCP_CLIENT); + connect(vc_conn:MGCP_PROC, vc_MGCP:MGCP_PROC); + /* GSUP part */ + connect(vc_conn:GSUP, vc_GSUP:GSUP_CLIENT); + connect(vc_conn:GSUP_PROC, vc_GSUP:GSUP_PROC); + + /* We cannot use vc_conn.start(f_init_handler(fn, id, pars)); as we cannot have + * a stand-alone 'derefers()' call, see https://www.eclipse.org/forums/index.php/t/1091364/ */ + vc_conn.start(derefers(fn)(id, pars)); + return vc_conn; +} + +function f_vty_config(TELNETasp_PT pt, charstring config_node, charstring cmd) +{ + /* enter config mode; enter node */ + f_vty_enter_config(pt); + f_vty_transceive(pt, config_node); + /* execute command */ + f_vty_transceive(pt, cmd); + /* leave config mode */ + f_vty_transceive(pt, "end"); +} + +private function f_tc_lu_imsi_noauth_tmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(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 { + f_init_handler(pars); + f_perform_lu(false, false, true); +} +testcase TC_lu_imsi_noauth_notmsi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "msc", "no assign-tmsi"); + + 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 { + f_init_handler(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; + } + } + f_expect_clear(); +} +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 { + f_init_handler(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; + } + } + f_expect_clear(); +} +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 { + f_init_handler(pars); + f_perform_lu(true, true, true); +} +testcase TC_lu_imsi_auth_tmsi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "network", "authentication required"); + + 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 { + f_init_handler(pars); + + 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(CM_TYPE_MO_CALL, 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"); } + } + + f_expect_clear(); +} +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(), 6); + vc_conn.done; +} + +private function f_tc_lu_and_mo_call(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + cpars.bss_rtp_port := 1110; + cpars.mgcp_connection_id_bss := '22222'H; + cpars.mgcp_connection_id_mss := '33333'H; + + f_perform_lu(cpars.expect_auth, true, true); + f_mo_call(cpars); +} +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(), 7); + vc_conn.done; +} + +/* Test LU (with authentication enabled), where HLR times out sending SAI response */ +private function f_tc_lu_auth_sai_timeout(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); + /* The HLR would normally return an auth vector here, but we fail to do so. */ + + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)); + f_expect_clear(); +} +testcase TC_lu_auth_sai_timeout() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "network", "authentication required"); + + vc_conn := f_start_handler(refers(f_tc_lu_auth_sai_timeout), testcasename(), 8); + vc_conn.done; +} + +/* Test LU (with authentication enabled), where HLR rejects sending SAI error */ +private function f_tc_lu_auth_sai_err(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_SAI_ERR(g_pars.imsi, 13)); + + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)); + f_expect_clear(); +} +testcase TC_lu_auth_sai_err() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "network", "authentication required"); + + vc_conn := f_start_handler(refers(f_tc_lu_auth_sai_err), testcasename(), 9); + vc_conn.done; +} + +/* Test LU but BSC will send a clear request in the middle */ +private function f_tc_lu_clear_request(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + f_sleep(1.0); + /* send clear request in the middle of the LU */ + BSSAP.send(ts_BSSMAP_ClearRequest(0)); + BSSAP.receive(tr_BSSMAP_ClearCommand); + BSSAP.send(ts_BSSMAP_ClearComplete); + alt { + /* See https://osmocom.org/issues/2862 */ + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { repeat; } + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {} + } + setverdict(pass); +} +testcase TC_lu_clear_request() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_clear_request), testcasename(), 10); + vc_conn.done; +} + +/* Test LU but BSC will send a clear request in the middle */ +private function f_tc_lu_disconnect(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + f_sleep(1.0); + /* send clear request in the middle of the LU */ + BSSAP.send(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_REQ); + setverdict(pass); +} +testcase TC_lu_disconnect() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_disconnect), testcasename(), 11); + vc_conn.done; +} + + +/* Test LU but with illegal mobile identity type = IMEI */ +private function f_tc_lu_by_imei(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imei(g_pars.imei) + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + /* wait for LU reject, ignore any ID REQ */ + alt { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) { } + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req)) { repeat; } + } + /* wait for normal teardown */ + f_expect_clear(); +} +testcase TC_lu_by_imei() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_by_imei), testcasename(), 12); + vc_conn.done; +} + +/* Test LU by TMSI with unknown TMSI, expect (and answer) ID REQ. */ +private function f_tc_lu_tmsi_noauth_unknown(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_tmsi('01020304'O); /* FIXME: Random */ + var PDU_DTAP_MT dtap_mt; + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + /* Wait for + respond to ID REQ (IMSI) */ + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req('001'B))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_MM_ID_Rsp_IMSI(g_pars.imsi))); + + /* Expect MSC to do UpdateLocation to HLR; respond to it */ + GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn)); + GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi)); + GSUP.send(ts_GSUP_UL_RES(g_pars.imsi)); + + alt { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) { + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_TmsiRealloc_Cmpl)); + } + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) { + setverdict(fail, "Expected LU ACK, but received REJ"); + } + } + + /* wait for normal teardown */ + f_expect_clear(); +} +testcase TC_lu_by_tmsi_noauth_unknown() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_tmsi_noauth_unknown), testcasename(), 13); + vc_conn.done; +} + + +/* Test IMSI DETACH (MI=IMSI) */ +private function f_tc_imsi_detach_by_imsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(valueof(ts_ML3_MO_MM_IMSI_DET_Ind(mi))); + + /* Send Early Classmark, just for the fun of it? */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + /* wait for normal teardown */ + f_expect_clear(); +} +testcase TC_imsi_detach_by_imsi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_imsi_detach_by_imsi), testcasename(), 14); + vc_conn.done; +} + +/* Test IMSI DETACH (MI=TMSI) */ +private function f_tc_imsi_detach_by_tmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var MobileIdentityLV mi := valueof(ts_MI_TMSI_LV('01020304'O)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(valueof(ts_ML3_MO_MM_IMSI_DET_Ind(mi))); + + /* Send Early Classmark, just for the fun of it? */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + /* wait for normal teardown */ + f_expect_clear(); +} +testcase TC_imsi_detach_by_tmsi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_imsi_detach_by_tmsi), testcasename(), 15); + vc_conn.done; +} + +/* Test IMSI DETACH (MI=IMEI), which is illegal */ +private function f_tc_imsi_detach_by_imei(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(g_pars.imei)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(valueof(ts_ML3_MO_MM_IMSI_DET_Ind(mi))); + + /* Send Early Classmark, just for the fun of it? */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + /* wait for normal teardown */ + f_expect_clear(); +} +testcase TC_imsi_detach_by_imei() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_imsi_detach_by_imei), testcasename(), 16); + vc_conn.done; +} + + +/* helper function for an emergency call. caller passes in mobile identity to use */ +private function f_emerg_call(MobileIdentityLV mi) runs on BSC_ConnHdlr { + + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_EMERG_CALL, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC)); + + var hexstring called := '112'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_EMERG_SETUP(tid))); + 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)); + MNCC.send(ts_MNCC_SETUP_rsp(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 */ + f_expect_clear(); +} + +/* establish an emergency call by IMEI, no SIM inserted (and hence no IMSI) */ +private function f_tc_emerg_call_imei_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(g_pars.imei)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_EMERG_CALL, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ('05'O))); + f_expect_clear(); +} +testcase TC_emerg_call_imei_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_emerg_call_imei_reject), testcasename(), 17); + vc_conn.done; +} + +/* establish an emergency call by IMSI, SIM inserted (and hence IMSI) */ +private function f_tc_emerg_call_imsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + /* First perform location update to ensure subscriber is known */ + f_perform_lu(false, true, true); + /* Then issue emergency call identified by IMSI */ + f_emerg_call(valueof(ts_MI_IMSI_LV(g_pars.imsi))); +} +testcase TC_emerg_call_imsi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_emerg_call_imsi), testcasename(), 18); + vc_conn.done; +} + +/* CM Service Request for VGCS -> reject */ +private function f_tc_cm_serv_req_vgcs_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* First perform location update to ensure subscriber is known */ + f_perform_lu(false, true, true); + + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_VGCS, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ(int2oct(32,1)))); + f_expect_clear(); +} +testcase TC_cm_serv_req_vgcs_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cm_serv_req_vgcs_reject), testcasename(), 19); + vc_conn.done; +} + +/* CM Service Request for VBS -> reject */ +private function f_tc_cm_serv_req_vbs_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* First perform location update to ensure subscriber is known */ + f_perform_lu(false, true, true); + + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_VBS, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ(int2oct(32,1)))); + f_expect_clear(); +} +testcase TC_cm_serv_req_vbs_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cm_serv_req_vbs_reject), testcasename(), 20); + vc_conn.done; +} + +/* CM Service Request for LCS -> reject */ +private function f_tc_cm_serv_req_lcs_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* First perform location update to ensure subscriber is known */ + f_perform_lu(false, true, true); + + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_LCS, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ(int2oct(32,1)))); + f_expect_clear(); +} +testcase TC_cm_serv_req_lcs_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cm_serv_req_lcs_reject), testcasename(), 21); + vc_conn.done; +} + +/* CM Re-Establishment Request */ +private function f_tc_cm_reest_req_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* First perform location update to ensure subscriber is known */ + f_perform_lu(false, true, true); + + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_REEST_REQ(0, mi)); + f_bssap_compl_l3(l3_info); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ(int2oct(32,1)))); + f_expect_clear(); +} +testcase TC_cm_reest_req_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cm_reest_req_reject), testcasename(), 22); + vc_conn.done; +} + +/* Test LU (with authentication enabled), with wrong response from MS */ +private function f_tc_lu_auth_2G_fail(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) + + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ + f_bssap_compl_l3(l3_lu); + + /* Send Early Classmark, just for the fun of it */ + BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); + + var AuthVector vec := f_gen_auth_vec_2g(); + var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(vec.rand, vec.sres, vec.kc)); + GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple)); + + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ(vec.rand))); + /* Send back wrong auth response */ + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_2G('00000000'O))); + + /* Expect GSUP AUTH FAIL REP to HLR */ + GSUP.receive(tr_GSUP_AUTH_FAIL_IND(g_pars.imsi)); + + /* Expect LU REJECT with Cause == Illegal MS */ + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej('03'O))); + f_expect_clear(); +} +testcase TC_lu_auth_2G_fail() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "network", "authentication required"); + + vc_conn := f_start_handler(refers(f_tc_lu_auth_2G_fail), testcasename(), 23); + vc_conn.done; +} + +private function f_tc_lu_imsi_auth_tmsi_encr_13_13(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + f_perform_lu(true, true, true, true); +} +testcase TC_lu_imsi_auth_tmsi_encr_13_13() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "network", "authentication required"); + f_vty_config(MSCVTY, "network", "encryption a5 1 3"); + + vc_conn := f_start_handler(refers(f_tc_lu_imsi_auth_tmsi_encr_13_13), testcasename(), 24); + vc_conn.done; +} + +/* Test Complete L3 without payload */ +private function f_tc_cl3_no_payload(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* Send Complete L3 Info with empty L3 frame */ + BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own, + valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, ''O)))); + + timer T := 5.0; + T.start; + alt { + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {} + /* Expect LU REJECT with Cause == Illegal MS */ + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + } + [] T.timeout { + setverdict(inconc, "Timeout waiting for ClearCommand or SCCP Release"); + self.stop; + } + } + setverdict(pass); +} +testcase TC_cl3_no_payload() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cl3_no_payload), testcasename(), 24); + vc_conn.done; +} + +/* Test Complete L3 with random payload */ +private function f_tc_cl3_rnd_payload(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var integer len := float2int(rnd() * 256.0); + var octetstring payl := f_rnd_octstring(len); + + /* Send Complete L3 Info with empty L3 frame */ + BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own, + valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, payl)))); + + timer T := 5.0; + T.start; + alt { + /* Immediate disconnect */ + [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {} + /* Expect LU REJECT with Cause == Illegal MS */ + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + } + [] BSSAP.receive(tr_PDU_DTAP_MT(?)) { repeat; } + [] T.timeout { + setverdict(inconc, "Timeout waiting for ClearCommand or SCCP Release"); + self.stop; + } + } + setverdict(pass); +} +testcase TC_cl3_rnd_payload() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_cl3_rnd_payload), testcasename(), 24); + vc_conn.done; +} + +/* Test Complete L3 with random payload */ +private function f_tc_establish_and_nothing(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + f_perform_lu(false, true, true, false); + + f_establish_fully(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false, false); + f_expect_clear(); +} +testcase TC_establish_and_nothing() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_establish_and_nothing), testcasename(), 25); + vc_conn.done; +} + +/* Test MO Call SETUP with no response from MNCC */ +private function f_tc_mo_setup_and_nothing(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + + f_perform_lu(false, true, true, false); + + f_establish_fully(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false, false); + f_create_mncc_expect(hex2str(cpars.called_party)); + f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); + + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party))); + + f_expect_clear(30.0); +} +testcase TC_mo_setup_and_nothing() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_mo_setup_and_nothing), testcasename(), 26); + vc_conn.done; +} + +/* Test MO Call with no response to RAN-side CRCX */ +private function f_tc_mo_crcx_ran_timeout(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + + f_perform_lu(false, true, true, false); + + f_establish_fully(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false, false); + f_create_mncc_expect(hex2str(cpars.called_party)); + f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); + + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party))); + MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc; + cpars.mncc_callref := mncc.u.signal.callref; + MNCC.send(ts_MNCC_CALL_PROC_req(cpars.mncc_callref, cpars.mncc_bearer_cap)); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(cpars.transaction_id))); + + MGCP.receive(tr_CRCX) -> value mgcp_cmd; + cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd); + cpars.mgcp_ep := mgcp_cmd.line.ep; + /* never respond to this */ + + f_expect_clear(30.0); +} +testcase TC_mo_crcx_ran_timeout() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_mo_crcx_ran_timeout), testcasename(), 27); + vc_conn.done; +} + +/* Test MO Call with reject to RAN-side CRCX */ +private function f_tc_mo_crcx_ran_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + + f_perform_lu(false, true, true, false); + + f_establish_fully(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false, false); + f_create_mncc_expect(hex2str(cpars.called_party)); + f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); + + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party))); + MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc; + cpars.mncc_callref := mncc.u.signal.callref; + MNCC.send(ts_MNCC_CALL_PROC_req(cpars.mncc_callref, cpars.mncc_bearer_cap)); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(cpars.transaction_id))); + + MGCP.receive(tr_CRCX) -> value mgcp_cmd; + cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd); + cpars.mgcp_ep := mgcp_cmd.line.ep; + /* Respond to CRCX with error */ + var MgcpResponse mgcp_rsp := { + line := { + code := "542", + trans_id := mgcp_cmd.line.trans_id, + string := "FORCED_FAIL" + }, + params := omit, + sdp := omit + } + MGCP.send(mgcp_rsp); + + timer T := 30.0; + T.start; + alt { + [] T.timeout { setverdict(fail, "Timeout waiting for channel release"); self.stop; } + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + setverdict(pass); + } + [] BSSAP.receive { repeat; } + [] MNCC.receive { repeat; } + [] GSUP.receive { repeat; } + [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd { + MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id)); + f_create_mgcp_delete_ep(cpars.mgcp_ep); + repeat; + } + [] MGCP.receive { repeat; } + } +} +testcase TC_mo_crcx_ran_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_mo_crcx_ran_reject), testcasename(), 28); + vc_conn.done; +} + + +/* helper function to start a MT call: MNCC SETUP; Paging; DChan est.; DTAP SETUP */ +private function f_mt_call_start(inout CallParameters cpars) runs on BSC_ConnHdlr { + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + var OCT4 tmsi; + + f_perform_lu(false, true, true, false); + if (isvalue(g_pars.tmsi)) { + tmsi := g_pars.tmsi; + } else { + tmsi := 'FFFFFFFF'O; + } + f_bssmap_register_imsi(g_pars.imsi, tmsi); + + /* Allocate call reference and send SETUP via MNCC to MSC */ + cpars.mncc_callref := f_rnd_int(2147483648); + MNCC.send(ts_MNCC_SETUP_req(cpars.mncc_callref, hex2str(g_pars.msisdn), + hex2str(cpars.called_party), hex2str(g_pars.imsi))); + + /* MSC->BSC: expect PAGING from MSC */ + BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)); + /* MS -> MSC: PAGING RESPONSE */ + f_establish_fully_pag(valueof(ts_MI_IMSI_LV(g_pars.imsi)), false, false); + + f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); + + /* MSC->MS: SETUP */ + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_SETUP(cpars.transaction_id, *, cpars.called_party))); +} + +/* Test MT Call */ +private function f_tc_mt_crcx_ran_reject(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('123456'H, 0)); + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + + f_mt_call_start(cpars); + + /* MS->MSC: CALL CONFIRMED */ + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CALL_CONF(cpars.transaction_id))); + + MNCC.receive(tr_MNCC_CALL_CONF_ind(cpars.mncc_callref)); + + MGCP.receive(tr_CRCX) -> value mgcp_cmd; + cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd); + cpars.mgcp_ep := mgcp_cmd.line.ep; + /* Respond to CRCX with error */ + var MgcpResponse mgcp_rsp := { + line := { + code := "542", + trans_id := mgcp_cmd.line.trans_id, + string := "FORCED_FAIL" + }, + params := omit, + sdp := omit + } + MGCP.send(mgcp_rsp); + + timer T := 30.0; + T.start; + alt { + [] T.timeout { setverdict(fail, "Timeout waiting for channel release"); self.stop; } + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + setverdict(pass); + } + [] BSSAP.receive { repeat; } + [] MNCC.receive { repeat; } + [] GSUP.receive { repeat; } + [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd { + MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id)); + f_create_mgcp_delete_ep(cpars.mgcp_ep); + repeat; + } + [] MGCP.receive { repeat; } + } +} +testcase TC_mt_crcx_ran_reject() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_mt_crcx_ran_reject), testcasename(), 29); + vc_conn.done; +} + + +/* Test MT Call T310 timer */ +private function f_tc_mt_t310(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars, 200.0); + var CallParameters cpars := valueof(t_CallParams('123456'H, 0)); + var MNCC_PDU mncc; + var MgcpCommand mgcp_cmd; + + f_mt_call_start(cpars); + + /* MS->MSC: CALL CONFIRMED */ + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CALL_CONF(cpars.transaction_id))); + MNCC.receive(tr_MNCC_CALL_CONF_ind(cpars.mncc_callref)); + + MGCP.receive(tr_CRCX) -> value mgcp_cmd; + cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd); + cpars.mgcp_ep := mgcp_cmd.line.ep; + /* FIXME: Respond to CRCX */ + + /* old libosmocore T310 default timeout is 180s. so let's wait 190 */ + timer T := 190.0; + T.start; + alt { + [] T.timeout { setverdict(fail, "Timeout waiting for T310"); self.stop; } + [] MNCC.receive(tr_MNCC_DISC_ind(cpars.mncc_callref)) { + MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23)))); + } + } + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(cpars.transaction_id))); + BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id))); + /* FIXME: We're sending this with TIflag 0: allocated by sender, which is wrong */ + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id))); + + alt { + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + setverdict(pass); + } + [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd { + MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id)); + f_create_mgcp_delete_ep(cpars.mgcp_ep); + repeat; + } + } +} +testcase TC_mt_t310() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_mt_t310), testcasename(), 30); + vc_conn.done; +} + +/* Perform successful LU + MO call, then GSUP LocationCancel. Subscriber must be denied CM SERV */ +private function f_tc_gsup_cancel(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + cpars.bss_rtp_port := 1110; + cpars.mgcp_connection_id_bss := '22222'H; + cpars.mgcp_connection_id_mss := '33333'H; + + /* Location Update to make subscriber known */ + f_perform_lu(cpars.expect_auth, true, true); + + /* First MO call should succeed */ + f_mo_call(cpars); + + /* Cancel the subscriber in the VLR */ + GSUP.send(ts_GSUP_CL_REQ(g_pars.imsi, OSMO_GSUP_CANCEL_TYPE_WITHDRAW)); + alt { + [] GSUP.receive(tr_GSUP_CL_RES(g_pars.imsi)) { } + [] GSUP.receive(tr_GSUP_CL_ERR(g_pars.imsi)) { + setverdict(fail, "Received GSUP Location Cancel Error"); + self.stop; + } + } + + /* Follow-up transactions should fail */ + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); + f_bssap_compl_l3(l3_info); + alt { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ)) { } + [] BSSAP.receive { + setverdict(fail, "Received unexpected BSSAP instead of CM SERV REJ"); + self.stop; + } + } + setverdict(pass); +} +testcase TC_gsup_cancel() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_gsup_cancel), testcasename(), 31); + vc_conn.done; +} + + + +/* TODO: + * continue to send repeated MO signalling messages to keep channel open: does MSC tmeout? + * malformed messages (missing IE, invalid message type): properly rejected? + * MT call while LU or is ongoing: Do we use existing lchan or page while lchan active? + * 3G/2G auth permutations + * encryption algorithms vs. classmark vs. vty config + * send new transaction after/during clear (like SMS, ...) + * too long L3 INFO in DTAP + * too long / padded BSSAP + * too long / short TLV values + */ + + +control { + 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_imsi_auth_tmsi() ); + execute( TC_cmserv_imsi_unknown() ); + execute( TC_lu_and_mo_call() ); + execute( TC_lu_auth_sai_timeout() ); + execute( TC_lu_auth_sai_err() ); + execute( TC_lu_clear_request() ); + execute( TC_lu_disconnect() ); + execute( TC_lu_by_imei() ); + execute( TC_lu_by_tmsi_noauth_unknown() ); + execute( TC_imsi_detach_by_imsi() ); + execute( TC_imsi_detach_by_tmsi() ); + execute( TC_imsi_detach_by_imei() ); + execute( TC_emerg_call_imei_reject() ); + execute( TC_emerg_call_imsi() ); + execute( TC_cm_serv_req_vgcs_reject() ); + execute( TC_cm_serv_req_vbs_reject() ); + execute( TC_cm_serv_req_lcs_reject() ); + execute( TC_cm_reest_req_reject() ); + execute( TC_lu_auth_2G_fail() ); + execute( TC_lu_imsi_auth_tmsi_encr_13_13() ); + execute( TC_cl3_no_payload() ); + execute( TC_cl3_rnd_payload() ); + execute( TC_establish_and_nothing() ); + execute( TC_mo_setup_and_nothing() ); + execute( TC_mo_crcx_ran_timeout() ); + execute( TC_mo_crcx_ran_reject() ); + execute( TC_mt_crcx_ran_reject() ); + execute( TC_mt_t310() ); + execute( TC_gsup_cancel() ); +} + + +} diff --git a/msc/gen_links.sh b/msc/gen_links.sh new file mode 100755 index 00000000..42499476 --- /dev/null +++ b/msc/gen_links.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +BASEDIR=../deps + +gen_links() { + DIR=$1 + FILES=$* + for f in $FILES; do + echo "Linking $f" + ln -sf $DIR/$f $f + done +} + +DIR=$BASEDIR/titan.TestPorts.UNIX_DOMAIN_SOCKETasp/src +FILES="UD_PT.cc UD_PT.hh UD_PortType.ttcn UD_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src +FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCConversion.hh TCCInterface.cc TCCInterface_ip.h" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src +FILES="Socket_API_Definitions.ttcn" +gen_links $DIR $FILES + +# Required by MGCP and IPA +DIR=$BASEDIR/titan.TestPorts.IPL4asp/src +FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh" +gen_links $DIR $FILES + +# required by M3UA_Emulation +DIR=$BASEDIR/titan.ProtocolModules.M3UA/src +FILES="M3UA_Types.ttcn" +gen_links $DIR $FILES + +# required by M3UA_Emulation +DIR=$BASEDIR/titan.TestPorts.SCTPasp/src +FILES="SCTPasp_PT.cc SCTPasp_PT.hh SCTPasp_PortType.ttcn SCTPasp_Types.ttcn" +gen_links $DIR $FILES + +# required by M3UA Emulation +DIR=../MTP3asp_CNL113337/src +FILES="MTP3asp_PortType.ttcn MTP3asp_Types.ttcn" +gen_links $DIR $FILES + +# required by SCCP Emulation +DIR=../M3UA_CNL113537/src +FILES="M3UA_Emulation.ttcn" +gen_links $DIR $FILES + +DIR=../SCCP_CNL113341/src +FILES="SCCP_Emulation.ttcn SCCP_EncDec.cc SCCP_Mapping.ttcnpp SCCP_Types.ttcn SCCPasp_Types.ttcn" +gen_links $DIR $FILES +ln -s SCCP_Mapping.ttcnpp SCCP_Mapping.ttcn + +DIR=$BASEDIR/titan.ProtocolModules.BSSMAP_v11.2.0/src +FILES="BSSAP_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.MobileL3_v13.4.0/src +FILES="MobileL3_CC_Types.ttcn MobileL3_CommonIE_Types.ttcn MobileL3_GMM_SM_Types.ttcn MobileL3_MM_Types.ttcn MobileL3_RRM_Types.ttcn MobileL3_SMS_Types.ttcn MobileL3_SS_Types.ttcn MobileL3_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.SDP/src +FILES="SDP_EncDec.cc SDP_Types.ttcn SDP_parse_.tab.c SDP_parse_.tab.h SDP_parse_parser.h SDP_parser.l +SDP_parser.y lex.SDP_parse_.c" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.RTP/src +FILES="RTP_EncDec.cc RTP_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.TELNETasp/src +FILES="TELNETasp_PT.cc TELNETasp_PT.hh TELNETasp_PortType.ttcn" +gen_links $DIR $FILES + +DIR=../library +FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn MNCC_Types.ttcn MNCC_EncDec.cc MNCC_CodecPort.ttcn mncc.h MNCC_Emulation.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc " +FILES+="IPA_Types.ttcn IPA_Emulation.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc RSL_Types.ttcn GSUP_Types.ttcn GSUP_Emulation.ttcn " +FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn L3_Templates.ttcn L3_Templates.ttcn " +FILES+="BSSMAP_Emulation.ttcn BSSAP_CodecPort.ttcn BSSMAP_Templates.ttcn BSSAP_Adapter.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_Emulation.ttcn IPA_Emulation.ttcn " +FILES+="RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunctDef.cc " +FILES+="MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunctDef.cc " +gen_links $DIR $FILES diff --git a/msc/regen_makefile.sh b/msc/regen_makefile.sh new file mode 100755 index 00000000..dacd10b5 --- /dev/null +++ b/msc/regen_makefile.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +FILES="*.ttcn SCCP_EncDec.cc SCTPasp_PT.cc TCCConversion.cc TCCInterface.cc UD_PT.cc MNCC_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc SDP_EncDec.cc RTP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc RTP_CodecPort_CtrlFunctDef.cc MGCP_CodecPort_CtrlFunctDef.cc TELNETasp_PT.cc Native_FunctionDefs.cc *.c" + +../regen-makefile.sh MSC_Tests.ttcn $FILES -- cgit v1.2.3