diff options
Diffstat (limited to 'msc/MSC_Tests.ttcn')
-rw-r--r-- | msc/MSC_Tests.ttcn | 1547 |
1 files changed, 1365 insertions, 182 deletions
diff --git a/msc/MSC_Tests.ttcn b/msc/MSC_Tests.ttcn index 5129c5bd..283ced68 100644 --- a/msc/MSC_Tests.ttcn +++ b/msc/MSC_Tests.ttcn @@ -13,9 +13,12 @@ module MSC_Tests { */ friend module MSC_Tests_Iu; +friend module MSC_Tests_ASCI; import from General_Types all; import from Osmocom_Types all; +import from Misc_Helpers all; +import from GSM_Types all; import from M3UA_Types all; import from M3UA_Emulation all; @@ -44,9 +47,11 @@ import from MGCP_Emulation all; import from MGCP_Types all; import from MGCP_Templates all; import from SDP_Types all; +import from SDP_Templates all; -import from GSUP_Emulation all; import from GSUP_Types all; +import from GSUP_Templates all; +import from GSUP_Emulation all; import from IPA_Emulation all; import from BSSAP_Types all; @@ -139,6 +144,7 @@ modulepar { charstring mp_smpp_password := "osmocom1"; charstring mp_mme_name := "mmec01.mmegi0001.mme.epc.mnc070.mcc901.3gppnetwork.org"; charstring mp_vlr_name := "vlr.example.net"; + integer mp_bssap_reset_retries := 1; RAN_Configurations mp_bssap_cfg := { { @@ -162,10 +168,6 @@ modulepar { rctx := 1 } }; - - boolean mp_enable_cell_id_test := true; - - boolean mp_enable_crashing_tests := true; } /* altstep for the global guard timer (only used when BSSAP_DIRECT @@ -179,14 +181,15 @@ private altstep as_Tguard_direct() runs on MTC_CT { private altstep as_optional_cc_rel(CallParameters cpars, boolean respond := false) runs on BSC_ConnHdlr { [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id))) { - if (respond) { - var BIT1 tid_remote := '1'B; - if (cpars.mo_call) { - tid_remote := '0'B; - } - BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id, tid_remote))); + if (respond) { + var BIT1 tid_remote := '1'B; + if (cpars.mo_call) { + tid_remote := '0'B; } + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id, tid_remote))); } + repeat; + } } function f_init_smpp(charstring id) runs on MTC_CT { @@ -307,10 +310,34 @@ function f_init(integer num_bsc := 1, boolean sgsap := false, boolean gsup := tr testcase.stop("excess number of BSC instances requested"); } + f_ipa_ctrl_start_client(mp_msc_ip, mp_msc_ctrl_port); + + 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, "msc", "check-imei-rqd 0"); + f_vty_config(MSCVTY, "network", "encryption a5 0"); + f_vty_config(MSCVTY, "cs7 instance 0", "sccp-timer ias " & int2str(g_msc_sccp_timer_ias)); + f_vty_config(MSCVTY, "cs7 instance 0", "sccp-timer iar " & int2str(g_msc_sccp_timer_iar)); + if (osmux) { + f_vty_config(MSCVTY, "msc", "osmux on"); + } else { + f_vty_config(MSCVTY, "msc", "osmux off"); + } + + /* Configure the MGCP timeout so that a failure to set up all RTP streams triggers within the time that we keep + * an otherwise established call open. */ + f_vty_config(MSCVTY, "msc", "timer mgw X2 3"); + for (var integer i := 0; i < num_bsc; i := i + 1) { if (isbound(mp_bssap_cfg[i])) { var RanOps ranops := BSC_RanOps; ranops.use_osmux := osmux; + ranops.bssap_reset_retries := mp_bssap_reset_retries; f_ran_adapter_init(g_bssap[i], mp_bssap_cfg[i], "MSC_Test_" & int2str(i), ranops); f_ran_adapter_start(g_bssap[i]); } else { @@ -318,7 +345,6 @@ function f_init(integer num_bsc := 1, boolean sgsap := false, boolean gsup := tr } } - f_ipa_ctrl_start(mp_msc_ip, mp_msc_ctrl_port); f_init_mncc("MSC_Test"); f_init_mgcp("MSC_Test"); @@ -331,22 +357,6 @@ function f_init(integer num_bsc := 1, boolean sgsap := false, boolean gsup := tr f_init_sgsap("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, "msc", "check-imei-rqd 0"); - f_vty_config(MSCVTY, "network", "encryption a5 0"); - f_vty_config(MSCVTY, "cs7 instance 0", "sccp-timer ias " & int2str(g_msc_sccp_timer_ias)); - f_vty_config(MSCVTY, "cs7 instance 0", "sccp-timer iar " & int2str(g_msc_sccp_timer_iar)); - if (osmux) { - f_vty_config(MSCVTY, "msc", "osmux on"); - } else { - f_vty_config(MSCVTY, "msc", "osmux off"); - } } /* Initialize for a direct connection to BSSAP. This function is an alternative @@ -370,7 +380,11 @@ function f_init_pars(integer imsi_suffix, boolean sgsap := false, boolean gsup : runs on MTC_CT return BSC_ConnHdlrPars { var BSC_ConnHdlrNetworkPars net_pars := { kc_support := '0A'O, /* A5/1 and A5/3 enabled */ + net_config := { "authentication optional", "encryption a5 0" }, + expect_attach_success := true, expect_tmsi := true, + expect_auth_attempt := false, + hlr_has_auth_info := true, expect_auth := false, expect_ciph := false, expect_imei := false, @@ -390,6 +404,7 @@ runs on MTC_CT return BSC_ConnHdlrPars { cm2 := valueof(ts_CM2_default), cm3 := omit, vec := omit, + vec_keep := false, net := net_pars, send_early_cm := true, ipa_ctrl_ip := mp_msc_ip, @@ -404,7 +419,9 @@ runs on MTC_CT return BSC_ConnHdlrPars { ran_is_geran := ran_is_geran, use_osmux := use_osmux, use_ipv6 := false, - verify_cell_id := mp_enable_cell_id_test and verify_cell_id + use_csd := false, + verify_cell_id := verify_cell_id, + common_id_last_eutran_plmn := omit }; if (not ran_is_geran) { pars.use_umts_aka := true; @@ -413,14 +430,15 @@ runs on MTC_CT return BSC_ConnHdlrPars { return pars; } -function f_start_handler_with_pars(void_fn fn, BSC_ConnHdlrPars pars, integer bssap_idx := 0) runs on MTC_CT return BSC_ConnHdlr { +function f_start_handler_create(BSC_ConnHdlrPars pars) runs on MTC_CT return BSC_ConnHdlr { var BSC_ConnHdlr vc_conn; - var charstring id := testcasename() & int2str(bssap_idx); + var charstring id := testcasename() & int2str(pars.ran_idx); vc_conn := BSC_ConnHdlr.create(id); + /* BSSMAP part / A interface */ - connect(vc_conn:BSSAP, g_bssap[pars.ran_idx + bssap_idx].vc_RAN:CLIENT); - connect(vc_conn:BSSAP_PROC, g_bssap[pars.ran_idx + bssap_idx].vc_RAN:PROC); + connect(vc_conn:BSSAP, g_bssap[pars.ran_idx].vc_RAN:CLIENT); + connect(vc_conn:BSSAP_PROC, g_bssap[pars.ran_idx].vc_RAN:PROC); /* MNCC part */ connect(vc_conn:MNCC, vc_MNCC:MNCC_CLIENT); connect(vc_conn:MNCC_PROC, vc_MNCC:MNCC_PROC); @@ -440,10 +458,20 @@ function f_start_handler_with_pars(void_fn fn, BSC_ConnHdlrPars pars, integer bs connect(vc_conn:SGsAP, vc_SGsAP:SGsAP_CLIENT); connect(vc_conn:SGsAP_PROC, vc_SGsAP:SGsAP_PROC); } + return vc_conn; +} +function f_start_handler_run(BSC_ConnHdlr vc_conn, void_fn fn, BSC_ConnHdlrPars pars) runs on MTC_CT { + var charstring id := testcasename() & int2str(pars.ran_idx); /* 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)); +} + +function f_start_handler_with_pars(void_fn fn, BSC_ConnHdlrPars pars) runs on MTC_CT return BSC_ConnHdlr { + var BSC_ConnHdlr vc_conn; + vc_conn := f_start_handler_create(pars); + f_start_handler_run(vc_conn, fn, pars); return vc_conn; } @@ -935,6 +963,66 @@ testcase TC_lu_clear_request() runs on MTC_CT { vc_conn.done; } +/* Test reaction on Clear Request during a MO Call */ +friend function f_TC_mo_mt_call_clear_request(charstring id, BSC_ConnHdlrPars pars) +runs on BSC_ConnHdlr { + var CallParameters cpars := valueof(t_CallParams); + var MNCC_PDU mncc_pdu; + timer T := 2.0; + + f_init_handler(pars); + + f_perform_lu(); + + /* HACK: reducing code duplication ('66'H - MO, '68'H - MT) */ + if (pars.imsi == '262420002532766'H) + { f_mo_call_establish(cpars); } + else + { f_mt_call_establish(cpars); } + + /* Hold the line for a while... */ + f_sleep(2.0); + + /* BSC sends BSSMAP Clear Request (e.g. due to RR failure) */ + BSSAP.send(ts_BSSMAP_ClearRequest(1)); + + /* Expect (optional) CC RELEASE and Clear Command */ + var default ccrel := activate(as_optional_cc_rel(cpars)); + f_expect_clear(); + deactivate(ccrel); + + /* Expect RELease indication on the MNCC socket */ + T.start; + alt { + [] MNCC.receive(tr_MNCC_REL_ind(cpars.mncc_callref)) -> value mncc_pdu { + log("Rx MNCC REL.ind, cause := ", mncc_pdu.u.signal.cause); + setverdict(pass); + } + [] MNCC.receive(MNCC_PDU:?) -> value mncc_pdu { + setverdict(fail, "Rx unexpected MNCC PDU: ", mncc_pdu); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for MNCC REL.ind"); + } + } +} +testcase TC_mo_call_clear_request() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + + f_init(); + + vc_conn := f_start_handler(refers(f_TC_mo_mt_call_clear_request), 2532766); // '66'H - MO + vc_conn.done; +} +testcase TC_mt_call_clear_request() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + + f_init(); + + vc_conn := f_start_handler(refers(f_TC_mo_mt_call_clear_request), 2532768); // '68'H - MT + vc_conn.done; +} + /* Test LU but BSC will send a clear request in the middle */ friend function f_tc_lu_disconnect(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { f_init_handler(pars); @@ -1096,7 +1184,7 @@ private function f_tc_attached_imsi_lu_unknown_tmsi(charstring id, BSC_ConnHdlrP f_expect_mm_info(); /* wait for normal teardown */ - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id := false); /* Now the same IMSI is still attached in the VLR, and a LU with an unknown TMSI reveals the same IMSI only * later during ID Response. osmo-msc first creates a new vlr_subscr for the unknown TMSI, and as soon as the @@ -1133,7 +1221,7 @@ private function f_tc_attached_imsi_lu_unknown_tmsi(charstring id, BSC_ConnHdlrP f_expect_mm_info(); /* wait for normal teardown */ - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id := false); } testcase TC_attached_imsi_lu_unknown_tmsi() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -1343,7 +1431,9 @@ private function f_tc_cm_reest_req_reject(charstring id, BSC_ConnHdlrPars pars) 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_cl3_or_initial_ue(l3_info); - BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ(int2oct(32,1)))); + /* Older osmo-msc returns: GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32, + * newer osmo-msc with CM Re-Establish support returns: GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38 */ + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ( (int2oct(32,1), int2oct(38,1)) ))); f_expect_clear(); } testcase TC_cm_reest_req_reject() runs on MTC_CT { @@ -1663,8 +1753,8 @@ friend function f_tc_mt_crcx_ran_reject(charstring id, BSC_ConnHdlrPars pars) ru repeat; } [] MGCP.receive { repeat; } - [] as_clear_cmd_compl_disc(); - [] as_optional_cc_rel(cpars); + [] as_optional_cc_rel(cpars); /* repeats internally */ + [] as_expect_clear() { setverdict(pass); } } } testcase TC_mt_crcx_ran_reject() runs on MTC_CT { @@ -1679,46 +1769,89 @@ testcase TC_mt_crcx_ran_reject() runs on MTC_CT { /* Test MT Call T310 timer */ friend 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; + var PDU_BSSAP bssap; + timer T310; + f_init_handler(pars); + + /* Make sure X2 does not fire in this test. This test does not send a CN RTP port to osmo-msc, which will + * trigger X2 timeout. We want to test T310, so make X2 significantly longer than T310=30s. */ + f_vty_config(MSCVTY, "msc", "timer mgw X2 40"); + + /* Initiate a MT call, establish connection */ 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 */ + /* NOTE: MSC is expected to start T310 here */ - /* old libosmocore T310 default timeout is 180s. so let's wait 190 */ - timer T := 190.0; - T.start; + interleave { + /* MSC->MGW: CRCX (first) */ + [] MGCP.receive(tr_CRCX) -> value mgcp_cmd { + f_handle_crcx(cpars, mgcp_cmd); /* MSC<-MGW: OK */ + } + + /* BSC->BSC: BSSMAP ASSIGNMENT REQ */ + [] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, ?)) -> value bssap { + BSSAP.send(ts_BSSMAP_AssignmentComplete(omit, + aoip := f_ts_BSSMAP_IE_AoIP_TLA(cpars.bss_rtp_ip, cpars.bss_rtp_port), + speechCodec := ts_BSSMAP_IE_SpeechCodec({ ts_CodecFR }))); + } + + /* MSC->MGW: MDCX */ + [] MGCP.receive(tr_MDCX) -> value mgcp_cmd { + MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, + sdp := omit)); + } + + /* MSC->MGW: CRCX (second) */ + [] MGCP.receive(tr_CRCX) -> value mgcp_cmd { + f_handle_crcx(cpars, mgcp_cmd); /* MSC<-MGW: OK */ + } + + [] MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref)); + } + + /* Reschedule the guard timeout */ + g_Tguard.start(30.0 + 10.0); + + /* NOTE: the BSC is expected to respond with CC ALERTING at this state, so + * the MSC would stop T310. However, the idea is to verify T310 expiration + * here, so grab some popcorn and wait for MNCC DISC.ind. */ + T310.start(30.0 + 2.0); + var MNCC_PDU mncc_rx; alt { - [] T.timeout { - setverdict(fail, "Timeout waiting for T310"); + [] T310.timeout { + setverdict(fail, "Timeout waiting for MNCC DISC.ind due to T310"); mtc.stop; } [] MNCC.receive(tr_MNCC_DISC_ind(cpars.mncc_callref)) { MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23)))); + log("Rx MNCC DISC.ind, T310.read yields ", T310.read); + setverdict(pass); } + [] MNCC.receive(MNCC_PDU:?) -> value mncc_rx { + log("Rx ", mncc_rx); + setverdict(fail, "Expected MNCC DISC.ind, got some other MNCC message instead"); + mtc.stop; } + } + 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))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id, '1'B))); alt { [] 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); + // FIXME: f_create_mgcp_delete_ep(cpars.mgcp_ep); repeat; } - [] as_clear_cmd_compl_disc(); + [] as_expect_clear() { setverdict(pass); } } } testcase TC_mt_t310() runs on MTC_CT { @@ -1925,6 +2058,84 @@ testcase TC_lu_imsi_auth_tmsi_encr_13_2() runs on MTC_CT { vc_conn.done; } +/* A5/0 + A5/1 + A5/3 + a5/4 only permitted on network side, and MS with only A5/1 support */ +private function f_tc_lu_imsi_auth_tmsi_encr_0134_1(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + pars.net.expect_auth := true; + pars.net.expect_ciph := true; + pars.net.kc_support := '03'O; /* A5/0 + A5/1 */ + pars.cm1.a5_1 := '0'B; + pars.cm2.a5_1 := '0'B; + pars.cm2.classmarkInformationType2_oct5.a5_3 := '0'B; + pars.cm2.classmarkInformationType2_oct5.a5_2 := '0'B; + pars.cm2.classmarkInformationType2_oct5.cm3 := '0'B; + pars.cm3 := omit; + pars.use_umts_aka := true; + + f_init_handler(pars, 15.0); + f_perform_lu(); +} +testcase TC_lu_imsi_auth_tmsi_encr_0134_1() 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 0 1 3 4"); + + vc_conn := f_start_handler(refers(f_tc_lu_imsi_auth_tmsi_encr_0134_1), 39); + vc_conn.done; +} + +/* A5/0 + A5/1 + A5/3 + a5/4 only permitted on network side, and MS with A5/3 + A5/4 support */ +private function f_tc_lu_imsi_auth_tmsi_encr_0134_34(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + pars.net.expect_auth := true; + pars.net.expect_ciph := true; + pars.net.kc_support := '19'O; /* A5/3 + A5/4 */ + pars.cm1.a5_1 := '1'B; + pars.cm2.a5_1 := '1'B; + pars.cm2.classmarkInformationType2_oct5.a5_3 := '1'B; + pars.cm2.classmarkInformationType2_oct5.a5_2 := '0'B; + pars.cm2.classmarkInformationType2_oct5.cm3 := '1'B; + pars.cm3 := valueof(ts_CM3_default); + pars.use_umts_aka := true; + + f_init_handler(pars, 15.0); + f_perform_lu(); +} +testcase TC_lu_imsi_auth_tmsi_encr_0134_34() 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 0 1 3 4"); + + vc_conn := f_start_handler(refers(f_tc_lu_imsi_auth_tmsi_encr_0134_34), 40); + vc_conn.done; +} + +/* A5/0 + A5/1 + A5/3 + a5/4 only permitted on network side, and MS with A5/3 support but no CM3 */ +private function f_tc_lu_imsi_auth_tmsi_encr_0134_34_no_cm3(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + pars.net.expect_auth := true; + pars.net.expect_ciph := true; + pars.net.kc_support := '19'O; /* A5/3 + A5/4 */ + pars.cm1.a5_1 := '1'B; + pars.cm2.a5_1 := '1'B; + pars.cm2.classmarkInformationType2_oct5.a5_3 := '1'B; + pars.cm2.classmarkInformationType2_oct5.a5_2 := '0'B; + pars.cm2.classmarkInformationType2_oct5.cm3 := '0'B; + pars.cm3 := omit; + pars.use_umts_aka := true; + + f_init_handler(pars, 15.0); + f_perform_lu(); +} +testcase TC_lu_imsi_auth_tmsi_encr_0134_34_no_cm3() 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 0 1 3 4"); + + vc_conn := f_start_handler(refers(f_tc_lu_imsi_auth_tmsi_encr_0134_34_no_cm3), 41); + vc_conn.done; +} + /* A5/0 + A5/1 + A5/3 only permitted on network side, and MS with only A5/2 support */ private function f_tc_lu_imsi_auth_tmsi_encr_013_2(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { pars.net.expect_auth := true; @@ -2017,7 +2228,7 @@ friend function f_tc_lu_and_mt_call_already_paging(charstring id, BSC_ConnHdlrPa f_expect_paging(); log("MNCC signals MT call, before Paging Response"); - f_mt_call_initate(cpars); + f_mt_call_initiate(cpars); f_ran_register_imsi(g_pars.imsi, g_pars.tmsi); f_sleep(0.5); @@ -2330,11 +2541,7 @@ friend function f_tc_lu_and_mt_sms_paging_and_nothing(charstring id, BSC_ConnHdl timer T := 20.0; T.start alt { - [pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)) { - setverdict(fail, "paging seems not to stop!"); - mtc.stop; - } - [not pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { + [] as_paging_any() { setverdict(fail, "paging seems not to stop!"); mtc.stop; } @@ -2387,13 +2594,18 @@ friend function f_tc_lu_and_mt_sms_paging_repeated(charstring id, BSC_ConnHdlrPa timer T := 5.0; T.start; alt { - [g_pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)) { - setverdict(fail, "GERAN should not repeat Paging, but received a second Paging"); - mtc.stop; + [] as_paging() { + if (g_pars.ran_is_geran) { + setverdict(fail, "GERAN should not repeat Paging, but received a second Paging"); + mtc.stop; + } else { + log("UTRAN: second Paging received, as expected"); + setverdict(pass); } - [not g_pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { - log("UTRAN: second Paging received, as expected"); - setverdict(pass); + } + [] as_paging_any() { + setverdict(fail, "Rx unexpected BSSMAP/RANAP Paging"); + mtc.stop; } [] T.timeout { if (g_pars.ran_is_geran) { @@ -2582,8 +2794,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2638,8 +2849,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2726,8 +2936,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2789,8 +2998,7 @@ runs on BSC_ConnHdlr { mtc.stop; } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2850,24 +3058,20 @@ runs on BSC_ConnHdlr { /* Both transaction IDs shall be different */ if (spars1.tid == spars2.tid) { - log("Both DTAP transaction IDs shall be different"); - setverdict(fail); + setverdict(fail, "Both DTAP transaction IDs shall be different"); } /* Both SM-RP-MR values shall be different */ if (spars1.rp.msg_ref == spars2.rp.msg_ref) { - log("Both SM-RP-MR values shall be different"); - setverdict(fail); + setverdict(fail, "Both SM-RP-MR values shall be different"); } /* Both SM-RP-MR values shall be assigned */ if (spars1.rp.msg_ref == 'FF'O) { - log("Unassigned SM-RP-MR value for the 1st SMS"); - setverdict(fail); + setverdict(fail, "Unassigned SM-RP-MR value for the 1st SMS"); } if (spars2.rp.msg_ref == 'FF'O) { - log("Unassigned SM-RP-MR value for the 2nd SMS"); - setverdict(fail); + setverdict(fail, "Unassigned SM-RP-MR value for the 2nd SMS"); } /* Send the 1st RP-ACK and expect MT-forwardSM-Res on GSUP */ @@ -2881,8 +3085,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2898,8 +3101,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2954,8 +3156,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -2971,14 +3172,12 @@ runs on BSC_ConnHdlr { /* Both SM-RP-MR values shall be different */ if (spars_mo.rp.msg_ref == spars_mt.rp.msg_ref) { - log("Both SM-RP-MR values shall be different"); - setverdict(fail); + setverdict(fail, "Both SM-RP-MR values shall be different"); } /* SM-RP-MR value for MT SMS shall be assigned */ if (spars_mt.rp.msg_ref == 'FF'O) { - log("Unassigned SM-RP-MR value for the MT SMS"); - setverdict(fail); + setverdict(fail, "Unassigned SM-RP-MR value for the MT SMS"); } /* Trigger RP-ACK for MO SMMA by sending MO-forwardSM-Res */ @@ -2999,8 +3198,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -3063,8 +3261,7 @@ runs on BSC_ConnHdlr { setverdict(pass); } [] GSUP.receive { - log("RX unexpected GSUP message"); - setverdict(fail); + setverdict(fail, "Rx unexpected GSUP message"); mtc.stop; } } @@ -3086,6 +3283,65 @@ testcase TC_gsup_mt_multi_part_sms() runs on MTC_CT { f_vty_config(MSCVTY, "msc", "no sms-over-gsup"); } +/* Test X36 (LU delay) timer allowing to deliver MT SMS over the same connection */ +friend function f_TC_gsup_mt_sms_lu_delay(charstring id, BSC_ConnHdlrPars pars) +runs on BSC_ConnHdlr { + var SmsParameters spars := valueof(t_SmsPars); + + f_init_handler(pars); + + /* We need to inspect GSUP activity */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* Perform location update */ + f_perform_lu(expect_clear := false); + + /* Register an 'expect' for given IMSI (+TMSI) */ + f_ran_register_imsi(g_pars.imsi, g_pars.tmsi); + + var template GSUP_PDU mt_forwardSM_res := tr_GSUP_MT_FORWARD_SM_RES( + imsi := g_pars.imsi, + /* NOTE: MSC should assign RP-MR itself */ + sm_rp_mr := ? + ); + + /* Some delay to make sure the connection remains alive */ + f_sleep(0.5); + + /* Submit a MT SMS on GSUP */ + f_gsup_forwardSM_req(spars); + + /* Wait for MT SMS on DTAP */ + f_mt_sms_expect(spars); + + /* Send RP-ACK and expect MT-forwardSM-Res on GSUP */ + f_mt_sms_send_rp_ack(spars); + alt { + [] GSUP.receive(mt_forwardSM_res) { + log("RX MT-forwardSM-Res (RP-ACK)"); + setverdict(pass); + } + [] GSUP.receive { + setverdict(fail, "Rx unexpected GSUP message"); + mtc.stop; + } + } + + f_expect_clear(); +} +testcase TC_gsup_mt_sms_lu_delay() runs on MTC_CT { + var BSC_ConnHdlrPars pars; + var BSC_ConnHdlr vc_conn; + f_init(); + pars := f_init_pars(92); + f_vty_config(MSCVTY, "msc", "sms-over-gsup"); + f_vty_config(MSCVTY, "msc", "timer geran X36 2500"); /* 2.5s */ + vc_conn := f_start_handler_with_pars(refers(f_TC_gsup_mt_sms_lu_delay), pars); + vc_conn.done; + f_vty_config(MSCVTY, "msc", "timer geran X36 0"); + f_vty_config(MSCVTY, "msc", "no sms-over-gsup"); +} + /* convert GSM L3 TON to SMPP_TON enum */ function f_sm_ton_from_gsm(BIT3 ton) return SMPP_TON { select (ton) { @@ -3383,13 +3639,11 @@ runs on BSC_ConnHdlr { GSUP.send(gsup_req); T.start; alt { - [pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)) { - setverdict(pass); - } - [not pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { - setverdict(pass); + [] as_paging() { setverdict(pass); } + [] as_paging_any() { + setverdict(fail, "Rx unexpected BSSMAP/RANAP Paging"); + mtc.stop; } - /* We don't expect anything else */ [] as_unexp_gsup_or_bssap_msg(); [] T.timeout { setverdict(fail, "Timeout waiting for Paging Request"); @@ -3998,13 +4252,11 @@ runs on BSC_ConnHdlr { /* Send it to MSC and expect Paging Request */ TP.start; alt { - [pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)) { - setverdict(pass); - } - [not pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { - setverdict(pass); + [] as_paging() { setverdict(pass); } + [] as_paging_any() { + setverdict(fail, "Rx unexpected BSSMAP/RANAP Paging"); + mtc.stop; } - /* We don't expect anything else */ [] as_unexp_gsup_or_bssap_msg(); [] TP.timeout { setverdict(fail, "Timeout waiting for Paging Request"); @@ -4072,13 +4324,11 @@ runs on BSC_ConnHdlr { /* Expect Paging Request */ TP.start; alt { - [pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)) { - setverdict(pass); - } - [not pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { - setverdict(pass); + [] as_paging() { setverdict(pass); } + [] as_paging_any() { + setverdict(fail, "Rx unexpected BSSMAP/RANAP Paging"); + mtc.stop; } - /* We don't expect anything else */ [] as_unexp_gsup_or_bssap_msg(); [] TP.timeout { setverdict(fail, "Timeout waiting for Paging Request"); @@ -4230,7 +4480,7 @@ friend function f_tc_lu_with_invalid_mcc_mnc(charstring id, BSC_ConnHdlrPars par mtc.stop; } } - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id:=false); } testcase TC_lu_with_invalid_mcc_mnc() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -4282,7 +4532,7 @@ private function f_tc_cipher_complete_without_alg(charstring id, BSC_ConnHdlrPar /* TODO: Verify MSC is using the best cipher available! How? */ f_msc_lu_hlr(); - f_accept_reject_lu(); + as_accept_reject_lu(); f_expect_clear(); setverdict(pass); } @@ -4375,11 +4625,16 @@ private function f_ctrl_subscr_in_vlr(charstring imsi_or_msisdn) runs on BSC_Con return true; } -/* Perform a location updatye at the A-Interface and run some checks to confirm +/* Perform a Location Update at the A-Interface and run some checks to confirm * that everything is back to normal. */ private function f_sgsap_bssmap_screening() runs on BSC_ConnHdlr { var SmsParameters spars := valueof(t_SmsPars); + /* From now on, since we initiated LU from A-Interface, we expect no + * LastEutranPLMNId on Common Id, since the SGs interface should be gone + */ + g_pars.common_id_last_eutran_plmn := omit; + /* Perform a location update, the SGs association is expected to fall * back to NULL */ f_perform_lu(); @@ -4392,11 +4647,10 @@ private function f_sgsap_bssmap_screening() runs on BSC_ConnHdlr { f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging"); alt { - [g_pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi)); { - setverdict(pass); - } - [not g_pars.ran_is_geran] BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi))) { - setverdict(pass); + [] as_paging() { setverdict(pass); } + [] as_paging_any() { + setverdict(fail, "Rx unexpected BSSMAP/RANAP Paging"); + mtc.stop; } [] SGsAP.receive { setverdict(fail, "Received unexpected message on SGs"); @@ -4454,12 +4708,19 @@ function f_sgs_perform_lu() runs on BSC_ConnHdlr { var PDU_SGsAP lua; var PDU_SGsAP mm_info; var octetstring mm_info_dtap; + var GsmMcc mcc; + var GsmMnc mnc; + var template (omit) TrackingAreaIdentityValue tai := omit; /* tell GSUP dispatcher to send this IMSI to us */ f_create_gsup_expect(hex2str(g_pars.imsi)); - + if (g_pars.common_id_last_eutran_plmn != omit) { + f_dec_mcc_mnc(g_pars.common_id_last_eutran_plmn, mcc, mnc); + tai := ts_SGsAP_TAI(mcc, mnc, 555); + } lur := valueof(ts_SGsAP_LU_REQ(g_pars.imsi, mme_name, IMSI_attach, - ts_SGsAP_LAI('901'H, '70'H, 2342))); + ts_SGsAP_LAI('901'H, '70'H, 2342), + tai)); /* Old LAI, if MS sends it */ /* TMSI status, if MS has no valid TMSI */ /* IMEISV, if it supports "automatic device detection" */ @@ -4519,6 +4780,7 @@ testcase TC_sgsap_lu() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11811, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_lu), pars); vc_conn.done; } @@ -4646,6 +4908,7 @@ testcase TC_sgsap_expl_imsi_det_eps() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11814, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_expl_imsi_det_eps), pars); vc_conn.done; } @@ -4670,6 +4933,7 @@ testcase TC_sgsap_impl_imsi_det_eps() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11814, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_impl_imsi_det_eps), pars); vc_conn.done; } @@ -4697,6 +4961,7 @@ testcase TC_sgsap_expl_imsi_det_noneps() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11815, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_expl_imsi_det_noneps), pars); vc_conn.done; } @@ -4724,6 +4989,7 @@ testcase TC_sgsap_impl_imsi_det_noneps() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11815, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_impl_imsi_det_noneps), pars); vc_conn.done; } @@ -4775,6 +5041,7 @@ testcase TC_sgsap_paging_rej() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11816, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_paging_rej), pars); vc_conn.done; } @@ -4824,6 +5091,7 @@ testcase TC_sgsap_paging_subscr_rej() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11817, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_paging_subscr_rej), pars); vc_conn.done; } @@ -4868,6 +5136,7 @@ testcase TC_sgsap_paging_ue_unr() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11818, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_paging_ue_unr), pars); vc_conn.done; } @@ -4927,6 +5196,7 @@ testcase TC_sgsap_paging_and_nothing() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11819, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_paging_and_nothing), pars); vc_conn.done; } @@ -4985,6 +5255,7 @@ testcase TC_sgsap_paging_and_lu() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11820, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_paging_and_lu), pars); vc_conn.done; } @@ -5193,6 +5464,7 @@ testcase TC_sgsap_mt_sms() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11823, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_mt_sms), pars); vc_conn.done; } @@ -5223,6 +5495,7 @@ testcase TC_sgsap_mo_sms() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11824, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_mo_sms), pars); vc_conn.done; } @@ -5286,6 +5559,7 @@ testcase TC_sgsap_mt_sms_and_nothing() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11825, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_mt_sms_and_nothing), pars); vc_conn.done; } @@ -5346,11 +5620,12 @@ testcase TC_sgsap_mt_sms_and_reject() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1, true); pars := f_init_pars(11826, true); + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); vc_conn := f_start_handler_with_pars(refers(f_tc_sgsap_mt_sms_and_reject), pars); vc_conn.done; } -/* Perform an MT CSDB call including LU */ +/* Perform an MT CSFB call including LU */ private function f_mt_lu_and_csfb_call(charstring id, BSC_ConnHdlrPars pars, boolean bssmap_lu) runs on BSC_ConnHdlr { f_init_handler(pars); @@ -5362,6 +5637,7 @@ private function f_mt_lu_and_csfb_call(charstring id, BSC_ConnHdlrPars pars, boo if (bssmap_lu) { f_perform_lu(); } + pars.common_id_last_eutran_plmn := f_enc_mcc_mnc('901'H, '70'H); f_sgs_perform_lu(); f_sleep(1.0); @@ -5370,7 +5646,7 @@ private function f_mt_lu_and_csfb_call(charstring id, BSC_ConnHdlrPars pars, boo var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); /* Initiate a call via MNCC interface */ - f_mt_call_initate(cpars); + f_mt_call_initiate(cpars); /* Expect a paging request and respond accordingly with a service request */ SGsAP.receive(tr_SGsAP_PAGING_REQ(pars.imsi, vlr_name, CS_call_indicator, omit)); @@ -5510,16 +5786,26 @@ testcase TC_ho_inter_bsc_unknown_cell() runs on MTC_CT { private altstep as_mgcp_ack_all_mdcx(CallParameters cpars) runs on BSC_ConnHdlr { var MgcpCommand mgcp_cmd; [] MGCP.receive(tr_MDCX) -> value mgcp_cmd { - var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_ip, - hex2str(cpars.mgcp_call_id), "42", - cpars.mgw_conn_2.mgw_rtp_port, - { 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_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_2.mgcp_connection_id, sdp)); - repeat; - } + var charstring conn_id; + f_mgcp_find_param_entry(mgcp_cmd.params, "I", conn_id); + var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_ip, + hex2str(cpars.mgcp_call_id), "42", + cpars.mgw_conn_2.mgw_rtp_port, + { 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_MDCX_ACK(mgcp_cmd.line.trans_id, str2hex(conn_id), sdp)); + repeat; + } +} + +private altstep as_mgcp_ack_all_dlcx(CallParameters cpars) runs on BSC_ConnHdlr { + var MgcpCommand mgcp_cmd; + [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd { + MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id)); + repeat; + } } private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { @@ -5531,19 +5817,26 @@ private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs o cpars.mgw_conn_2.mgw_rtp_ip := "::2"; cpars.bss_rtp_ip := "::3"; } + if (pars.use_csd) { + f_set_cpars_csd(cpars, "BS25T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); + } f_init_handler(pars); f_vty_transceive(MSCVTY, "configure terminal"); f_vty_transceive(MSCVTY, "msc"); f_vty_transceive(MSCVTY, "neighbor a cgi 262 42 23 42 ran-pc 0.24.1"); - f_vty_transceive(MSCVTY, "neighbor a lac 5 ran-pc 0.24.2"); + f_vty_transceive(MSCVTY, "neighbor a cgi 023 42 5 6 ran-pc 0.24.2"); f_vty_transceive(MSCVTY, "exit"); f_vty_transceive(MSCVTY, "exit"); f_perform_lu(); f_mo_call_establish(cpars); + /* Remember the last n_sd (sequence number), + * we will need it when the other BSS hands over back to us. */ + var N_Sd_Array last_n_sd := f_bssmap_last_n_sd(); + f_sleep(1.0); var default ack_mdcx := activate(as_mgcp_ack_all_mdcx(cpars)); @@ -5552,7 +5845,7 @@ private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs o var BssmapCause cause := enum2int(cause_val); var template BSSMAP_FIELD_CellIdentificationList cil; - cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('023'H, '42'H, 5) } }; + cil := { cIl_CGI := { ts_BSSMAP_CI_CGI('023'H, '42'H, 5, 6) } }; /* old BSS sends Handover Required */ BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); @@ -5577,10 +5870,26 @@ private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs o /* Ok, that went well, now the other BSC is handovering back here -- * from now on this here is the new BSS. */ - f_create_bssmap_exp_handoverRequest(193); - + f_create_bssmap_exp_n_connect(193); + + var template BSSMAP_IE_EncryptionInformation encryptionInformation; + var template BSSMAP_IE_ChosenEncryptionAlgorithm chosenEncryptionAlgorithm; + var template BSSMAP_IE_KC128 kC128; + var OCT1 a5_perm_alg; + f_get_expected_encryption(encryptionInformation, chosenEncryptionAlgorithm, kC128, a5_perm_alg); + var template PDU_BSSAP expect_ho_request := tr_BSSMAP_HandoverRequest(encryptionInformation, + chosenEncryptionAlgorithm, + kC128, codecList := ?); var PDU_BSSAP ho_request; - BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request; + alt { + [] BSSAP.receive(expect_ho_request); + [] BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request { + log("Error: Wrong handoverRequest received. Expected: ", expect_ho_request, + " got ", ho_request); + setverdict(fail, "Wrong handoverRequest received"); + mtc.stop; + } + } /* new BSS composes a RR Handover Command */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); @@ -5604,13 +5913,11 @@ private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs o deactivate(ack_mdcx); - var default ccrel := activate(as_optional_cc_rel(cpars, true)); - - /* blatant cheating */ - var N_Sd_Array last_n_sd := f_bssmap_last_n_sd(); - last_n_sd[0] := 3; + /* Use the n_sd (sequence number) we stored before the first handover. + * Otherwise the MSC may treat our DTAP messages as duplicates and discard them. */ f_bssmap_continue_after_n_sd(last_n_sd); + var default ccrel := activate(as_optional_cc_rel(cpars, true)); f_call_hangup(cpars, true); f_sleep(1.0); deactivate(ccrel); @@ -5625,11 +5932,26 @@ private function f_tc_ho_inter_bsc1(charstring id, BSC_ConnHdlrPars pars) runs o bss_rtp_ip := "1.2.3.4"; } f_init_handler(pars); - f_create_bssmap_exp_handoverRequest(194); - + f_create_bssmap_exp_n_connect(194); + + var template BSSMAP_IE_EncryptionInformation encryptionInformation; + var template BSSMAP_IE_ChosenEncryptionAlgorithm chosenEncryptionAlgorithm; + var template BSSMAP_IE_KC128 kC128; + var OCT1 a5_perm_alg; + f_get_expected_encryption(encryptionInformation, chosenEncryptionAlgorithm, kC128, a5_perm_alg); + var template PDU_BSSAP expect_ho_request := tr_BSSMAP_HandoverRequest(encryptionInformation, + chosenEncryptionAlgorithm, + kC128, codecList := ?); var PDU_BSSAP ho_request; - BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request; - + alt { + [] BSSAP.receive(expect_ho_request); + [] BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request { + log("Error: Wrong handoverRequest received. Expected: ", expect_ho_request, + " got ", ho_request); + setverdict(fail, "Wrong handoverRequest received"); + mtc.stop; + } + } /* new BSS composes a RR Handover Command */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); @@ -5657,7 +5979,7 @@ private function f_tc_ho_inter_bsc1(charstring id, BSC_ConnHdlrPars pars) runs o var BssmapCause cause := enum2int(cause_val); var template BSSMAP_FIELD_CellIdentificationList cil; - cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('262'H, '42'H, 23) } }; + cil := { cIl_CGI := { ts_BSSMAP_CI_CGI('262'H, '42'H, 23, 42) } }; /* old BSS sends Handover Required */ BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); @@ -5676,27 +5998,70 @@ private function f_tc_ho_inter_bsc1(charstring id, BSC_ConnHdlrPars pars) runs o f_expect_clear(); setverdict(pass); } -function f_tc_ho_inter_bsc_main(boolean use_ipv6 := false) runs on MTC_CT { +function f_tc_ho_inter_bsc_main(boolean use_ipv6 := false, integer a5_n := 0, boolean use_csd := false) runs on MTC_CT { var BSC_ConnHdlr vc_conn0; var BSC_ConnHdlr vc_conn1; f_init(2); var BSC_ConnHdlrPars pars0 := f_init_pars(53); pars0.use_ipv6 := use_ipv6; + pars0.use_csd := use_csd; + pars0.net.expect_ciph := a5_n > 0; + pars0.net.expect_auth := pars0.net.expect_ciph; + pars0.net.kc_support := bit2oct('00000001'B << a5_n); + pars0.cm2.classmarkInformationType2_oct5.a5_3 := '1'B; + pars0.cm2.classmarkInformationType2_oct5.a5_2 := '0'B; + pars0.cm2.classmarkInformationType2_oct5.cm3 := '1'B; + pars0.cm3 := valueof(ts_CM3_default); + pars0.use_umts_aka := true; + pars0.vec := f_gen_auth_vec_3g(); + pars0.vec_keep := true; + pars0.ran_idx := 0; + var BSC_ConnHdlrPars pars1 := f_init_pars(53); pars1.use_ipv6 := use_ipv6; + pars1.use_csd := use_csd; + pars1.net.expect_ciph := pars0.net.expect_ciph; + pars1.net.expect_auth := pars0.net.expect_ciph; + pars1.net.kc_support := bit2oct('00000001'B << a5_n); + pars1.cm2 := pars0.cm2; + pars1.cm3 := pars0.cm3; + pars1.use_umts_aka := true; + /* Both components need the same auth vector info because we expect f_tc_ho_inter_bsc0's ciphering key to be + * identical to the one that shows up in f_tc_ho_inter_bsc1. Can only do that by feeding in a vector to both + * components and then not overwriting it in BSC_ConnectionHandler. */ + pars1.vec := pars0.vec; + pars1.vec_keep := true; + pars1.ran_idx := 1; + + if (a5_n > 0) { + f_vty_config(MSCVTY, "network", "authentication required"); + } + f_vty_config(MSCVTY, "network", "encryption a5 " & int2str(a5_n)); - vc_conn0 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc0), pars0, 0); - vc_conn1 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc1), pars1, 1); + vc_conn0 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc0), pars0); + vc_conn1 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc1), pars1); vc_conn0.done; vc_conn1.done; } testcase TC_ho_inter_bsc() runs on MTC_CT { - f_tc_ho_inter_bsc_main(false); + f_tc_ho_inter_bsc_main(false, a5_n := 0); +} +testcase TC_ho_inter_bsc_a5_1() runs on MTC_CT { + f_tc_ho_inter_bsc_main(false, a5_n := 1); +} +testcase TC_ho_inter_bsc_a5_3() runs on MTC_CT { + f_tc_ho_inter_bsc_main(false, a5_n := 3); +} +testcase TC_ho_inter_bsc_a5_4() runs on MTC_CT { + f_tc_ho_inter_bsc_main(false, a5_n := 4); } testcase TC_ho_inter_bsc_ipv6() runs on MTC_CT { f_tc_ho_inter_bsc_main(true); } +testcase TC_ho_inter_bsc_csd() runs on MTC_CT { + f_tc_ho_inter_bsc_main(use_csd := true); +} function f_ML3_patch_seq_nr_MS_NW(in uint2_t seq_nr, inout octetstring enc_l3) { log("MS_NW patching N(SD)=", seq_nr, " into dtap ", enc_l3); @@ -5706,12 +6071,16 @@ function f_ML3_patch_seq_nr_MS_NW(in uint2_t seq_nr, inout octetstring enc_l3) { private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { var CallParameters cpars; + var PDU_BSSAP ho_request; cpars := valueof(t_CallParams('12345'H, 0)); if (pars.use_ipv6) { - cpars.mgw_conn_1.mgw_rtp_ip := "::1"; - cpars.mgw_conn_2.mgw_rtp_ip := "::2"; - cpars.bss_rtp_ip := "::3"; + cpars.mgw_conn_1.mgw_rtp_ip := "::1"; + cpars.mgw_conn_2.mgw_rtp_ip := "::2"; + cpars.bss_rtp_ip := "::3"; + } + if (pars.use_csd) { + f_set_cpars_csd(cpars, "BS25T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); } var hexstring ho_number := f_gen_msisdn(99999); @@ -5736,7 +6105,7 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run var BssmapCause cause := enum2int(cause_val); var template BSSMAP_FIELD_CellIdentificationList cil; - cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('017'H, '017'H, 1) } }; + cil := { cIl_CGI := { ts_BSSMAP_CI_CGI('017'H, '017'H, 1, 1) } }; /* old BSS sends Handover Required */ BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); @@ -5744,21 +6113,47 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run /* The target cell 017-017 LAC 1 is configured to be a remote MSC of name "msc-017-017-1". * This MSC tries to reach the other MSC via GSUP. */ + var template BSSMAP_IE_EncryptionInformation encryptionInformation; + var template BSSMAP_IE_ChosenEncryptionAlgorithm chosenEncryptionAlgorithm; + var template BSSMAP_IE_KC128 kC128; + var OCT1 a5_perm_alg; + f_get_expected_encryption(encryptionInformation, chosenEncryptionAlgorithm, kC128, a5_perm_alg); + var template PDU_BSSAP expect_ho_request := tr_BSSMAP_HandoverRequest(encryptionInformation, + chosenEncryptionAlgorithm, + kC128, codecList := ?); + var octetstring remote_msc_name := '6D73632D3031372D3031372D3100'O; /* "msc-017-017-1\0" as octetstring */ var GSUP_PDU prep_ho_req; - GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST, - pars.imsi, destination_name := remote_msc_name)) -> value prep_ho_req; + alt { + [] GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST, + pars.imsi, destination_name := remote_msc_name, + an_apdu := t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, pdu := ?))) -> value prep_ho_req; + [] GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST)) { + setverdict(fail, "Wrong OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST message received"); + mtc.stop; + } + } var GSUP_IeValue source_name_ie; f_gsup_find_ie(prep_ho_req, OSMO_GSUP_SOURCE_NAME_IE, source_name_ie); var octetstring local_msc_name := source_name_ie.source_name; + /* Decode PDU to 1) match with expect_ho_request and 2) to forward the actual chosen encryption algorithm. */ + var GSUP_IeValue an_apdu_ie; + f_gsup_find_ie(prep_ho_req, OSMO_GSUP_AN_APDU_IE, an_apdu_ie); + ho_request := dec_PDU_BSSAP(an_apdu_ie.an_apdu.pdu); + if (not match(ho_request, expect_ho_request)) { + setverdict(fail, "Wrong PDU in OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST message received"); + mtc.stop; + } + /* Remote MSC has figured out its BSC and signals success */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); var PDU_BSSAP ho_req_ack := valueof(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), - aoIPTransportLayer := omit, - speechCodec := ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + aoIPTransportLayer := omit, + speechCodec := ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}), + chosenEncryptionAlgorithm := ho_request.pdu.bssmap.handoverRequest.chosenEncryptionAlgorithm)); GSUP.send(ts_GSUP_E_PrepareHandoverResult( pars.imsi, ho_number, @@ -5880,7 +6275,7 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run /* inter-MSC handover back to the first MSC */ - f_create_bssmap_exp_handoverRequest(193); + f_create_bssmap_exp_n_connect(193); cil := { cIl_CGI := { ts_BSSMAP_CI_CGI('262'H, '42'H, 23, 42) } }; /* old BSS sends Handover Required, via inter-MSC E link: like @@ -5894,7 +6289,19 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run )); /* MSC asks local BSS to prepare Handover to it */ - BSSAP.receive(tr_BSSMAP_HandoverRequest); + f_get_expected_encryption(encryptionInformation, chosenEncryptionAlgorithm, kC128, a5_perm_alg); + expect_ho_request := tr_BSSMAP_HandoverRequest(encryptionInformation, + chosenEncryptionAlgorithm, + kC128, codecList := ?); + alt { + [] BSSAP.receive(expect_ho_request) -> value ho_request; + [] BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request { + log("Error: Wrong handoverRequest received. Expected: ", expect_ho_request, + " got ", ho_request); + setverdict(fail, "Wrong handoverRequest received"); + mtc.stop; + } + } /* Make sure the new BSSAP conn continues with the correct N_SD sequence numbers */ f_bssmap_continue_after_n_sd(last_n_sd); @@ -5905,7 +6312,8 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(f_ts_BSSMAP_IE_AoIP_TLA(cpars.bss_rtp_ip, cpars.bss_rtp_port)); BSSAP.send(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), - tla, ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + tla, ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}), + chosenEncryptionAlgorithm := ho_request.pdu.bssmap.handoverRequest.chosenEncryptionAlgorithm)); /* HandoverCommand goes out via remote MSC-I */ var GSUP_PDU prep_subsq_ho_res; @@ -5942,15 +6350,40 @@ private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) run setverdict(pass); } -testcase TC_ho_inter_msc_out() runs on MTC_CT { +function f_tc_ho_inter_msc_out_a5(integer a5_n) runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1); var BSC_ConnHdlrPars pars := f_init_pars(54); + pars.net.expect_ciph := a5_n > 0; + pars.net.expect_auth := pars.net.expect_ciph; + pars.net.kc_support := bit2oct('00000001'B << a5_n); + pars.cm2.classmarkInformationType2_oct5.a5_3 := '1'B; + pars.cm2.classmarkInformationType2_oct5.a5_2 := '0'B; + pars.cm2.classmarkInformationType2_oct5.cm3 := '1'B; + pars.cm3 := valueof(ts_CM3_default); + pars.use_umts_aka := true; - vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars, 0); + if (a5_n > 0) { + f_vty_config(MSCVTY, "network", "authentication required"); + } + f_vty_config(MSCVTY, "network", "encryption a5 " & int2str(a5_n)); + + vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars); vc_conn.done; } +testcase TC_ho_inter_msc_out() runs on MTC_CT { + f_tc_ho_inter_msc_out_a5(0); +} +testcase TC_ho_inter_msc_out_a5_1() runs on MTC_CT { + f_tc_ho_inter_msc_out_a5(1); +} +testcase TC_ho_inter_msc_out_a5_3() runs on MTC_CT { + f_tc_ho_inter_msc_out_a5(3); +} +testcase TC_ho_inter_msc_out_a5_4() runs on MTC_CT { + f_tc_ho_inter_msc_out_a5(4); +} testcase TC_ho_inter_msc_out_ipv6() runs on MTC_CT { var BSC_ConnHdlr vc_conn; f_init(1); @@ -5958,7 +6391,17 @@ testcase TC_ho_inter_msc_out_ipv6() runs on MTC_CT { var BSC_ConnHdlrPars pars := f_init_pars(54); pars.use_ipv6 := true; - vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars, 0); + vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars); + vc_conn.done; +} +testcase TC_ho_inter_msc_out_csd() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(1); + + var BSC_ConnHdlrPars pars := f_init_pars(54); + pars.use_csd := true; + + vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars); vc_conn.done; } @@ -6043,7 +6486,7 @@ private function f_tc_lu_imsi_auth_tmsi_check_imei_nack(charstring id, BSC_ConnH f_msc_lu_hlr(); f_mm_imei(); f_expect_lu_reject(); - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id:=false); } testcase TC_lu_imsi_auth_tmsi_check_imei_nack() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -6073,7 +6516,7 @@ private function f_tc_lu_imsi_auth_tmsi_check_imei_err(charstring id, BSC_ConnHd f_msc_lu_hlr(); f_mm_imei(); f_expect_lu_reject(); - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id:=false); } testcase TC_lu_imsi_auth_tmsi_check_imei_err() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -6164,7 +6607,7 @@ private function f_tc_lu_imsi_auth_tmsi_check_imei_early_nack(charstring id, BSC f_mm_imei_early(); f_expect_lu_reject(); - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id:=false); } testcase TC_lu_imsi_auth_tmsi_check_imei_early_nack() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -6192,7 +6635,7 @@ private function f_tc_lu_imsi_auth_tmsi_check_imei_early_err(charstring id, BSC_ f_mm_imei_early(); f_expect_lu_reject(); - f_expect_clear(); + f_expect_clear(verify_vlr_cell_id:=false); } testcase TC_lu_imsi_auth_tmsi_check_imei_early_err() runs on MTC_CT { var BSC_ConnHdlr vc_conn; @@ -6227,15 +6670,13 @@ testcase TC_invalid_mgcp_crash() runs on MTC_CT { vc_conn.done; } +/* Test how the MSC handles a malformed MM IDENTITY RESPONSE with no identity. */ friend function f_tc_mm_id_resp_no_identity(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { - pars.tmsi := 'FFFFFFFF'O; f_init_handler(pars); - f_create_gsup_expect(hex2str(g_pars.imsi)); - /* Initiate Location Updating using an unknown TMSI */ - f_bssap_compl_l3(f_build_lu_tmsi(pars.tmsi)); + f_bssap_compl_l3(f_build_lu_tmsi('FFFFFFFF'O)); /* Expect an Identity Request, send response with no identity */ BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req(CM_ID_TYPE_IMSI))); @@ -6252,7 +6693,10 @@ runs on BSC_ConnHdlr { } }))); - f_expect_lu_reject(); + /* XXX: Current osmo-msc does not react on bad/malformed MM IDENTITY RESPONSE immediately. + * It instead relies on expiry of timer X1, which is set to 5.0 seconds by default. This + * is not good (DoS vector) and should ideally be fixed, but for now just work it around. */ + f_expect_lu_reject(Tval := 5.0 + 1.0); f_expect_clear(); } testcase TC_mm_id_resp_no_identity() runs on MTC_CT { @@ -6302,10 +6746,712 @@ testcase TC_lu_and_expire_while_paging() runs on MTC_CT { vc_conn.done; } +private altstep as_mncc_rx_rtp_create(CallParameters cpars) runs on BSC_ConnHdlr { + [] MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref)); +} + +const charstring REEST_LOST_CONNECTION := "REEST_LOST_CONNECTION"; +const charstring REEST_CLEARED := "REEST_CLEARED"; + +friend function f_tc_call_re_establishment_1(charstring id, BSC_ConnHdlrPars pars) + runs on BSC_ConnHdlr { + f_init_handler(pars, t_guard := 30.0); + + f_perform_lu(); + + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + f_mo_call_establish(cpars); + f_sleep(3.0); + COORD.send(REEST_LOST_CONNECTION); + COORD.send(cpars); + f_expect_clear(verify_vlr_cell_id := false); + COORD.send(REEST_CLEARED); +} + +friend function f_tc_call_re_establishment_2(charstring id, BSC_ConnHdlrPars pars) + runs on BSC_ConnHdlr { + f_init_handler(pars, t_guard := 30.0); + var CallParameters cpars; + + COORD.receive(REEST_LOST_CONNECTION); + COORD.receive(tr_CallParams) -> value cpars; + + f_gsup_change_connhdlr(hex2str(g_pars.imsi)); + f_create_smpp_expect(hex2str(pars.msisdn)); + + /* The MS has lost the first channel and decides to show up on a new conn (on a nearby neighbor cell) to ask for + * CM Re-Establishment. Send a Complete Layer 3 to osmo-msc with a CM Re-Establishment Request. */ + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + var PDU_ML3_MS_NW l3_info := valueof(ts_CM_REESTABL_REQ(mi)); + f_cl3_or_initial_ue(l3_info); + + /* At this point the other test component should receive the Clear Command for the first A connection. */ + + /* This new connection continues with Authentication... */ + f_mm_common(); + + /* ...and with Assignment of a voice channel. */ + var template BSSMAP_IE_AoIP_TransportLayerAddress tla_ass := + (f_tr_BSSMAP_IE_AoIP_TLA(cpars.mgw_conn_1.mgw_rtp_ip, ?), + f_tr_BSSMAP_IE_AoIP_TLA(cpars.mgw_conn_2.mgw_rtp_ip, ?)); + BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)); + /* By this Assignment Request, the CM Re-Establishment Request is implicitly accepted. */ + + /* Send Assignment Complete from BSC */ + var template BSSMAP_IE_AoIP_TransportLayerAddress tla; + tla := f_ts_BSSMAP_IE_AoIP_TLA(cpars.bss_rtp_ip, cpars.bss_rtp_port); + var BSSMAP_IE_SpeechCodec codec; + codec := valueof(ts_BSSMAP_IE_SpeechCodec({ts_CodecFR})); + + /* Make really sure the other component is done with its MGCP */ + COORD.receive(REEST_CLEARED); + + /* Transfer state for this call over to this test component so we can resolve MNCC and MGCP in this function. */ + f_mncc_change_connhdlr(cpars.mncc_callref); + f_mgcp_change_connhdlr(cpars.mgcp_ep); + + /* osmo-msc may redirect the MGW endpoint to the newly allocated channel. + * Apparently osmo-msc currently also sends an MDCX to the CN side, just repeating the same configuration that + * is already in use. This test accepts any number of or even lack of MDCX. */ + var default ack_mdcx := activate(as_mgcp_ack_all_mdcx(cpars)); + var default optional_rtp_create := activate(as_mncc_rx_rtp_create(cpars)); + + BSSAP.send(ts_BSSMAP_AssignmentComplete(omit, tla, codec)); + + /* The call has been fully re-established. + * Let a bit of time pass before hanging up, for everything to settle. */ + f_sleep(3.0); + + deactivate(optional_rtp_create); + deactivate(ack_mdcx); + + /* Hang up the call and clear the new, second A connection */ + var default ack_dlcx := activate(as_mgcp_ack_all_dlcx(cpars)); + + /* CC release. This is the proper MS initiated release sequence as shown by + * https://git.osmocom.org/osmo-msc/tree/doc/sequence_charts/voice_call_full.msc?id=e53ecde83e4fb2470209e818e9ad76a2d6a19190 + * f_call_hangup() seems a bit mixed up, so here a "proper" sequence. Fix of f_call_hangup() pending. */ + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_DISC(cpars.transaction_id, '0'B, '0000000'B))); + 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_RELEASE(cpars.transaction_id))); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id, '0'B))); + MNCC.receive(tr_MNCC_REL_cnf(cpars.mncc_callref, cause := *)); + + /* BSSAP clear */ + interleave { + [] BSSAP.receive(tr_BSSMAP_ClearCommand) { + BSSAP.send(ts_BSSMAP_ClearComplete); + } + [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + } + + f_sleep(1.0); + deactivate(ack_dlcx); +} + +testcase TC_call_re_establishment() runs on MTC_CT { + var BSC_ConnHdlr vc_conn1; + var BSC_ConnHdlr vc_conn2; + f_init(); + + var BSC_ConnHdlrPars pars1 := f_init_pars(91); + var BSC_ConnHdlrPars pars2 := pars1; + + vc_conn1 := f_start_handler_create(pars1); + vc_conn2 := f_start_handler_create(pars2); + connect(vc_conn1:COORD, vc_conn2:COORD); + f_start_handler_run(vc_conn1, refers(f_tc_call_re_establishment_1), pars1); + f_start_handler_run(vc_conn2, refers(f_tc_call_re_establishment_2), pars2); + vc_conn1.done; + vc_conn2.done; +} + +testcase TC_call_re_establishment_auth() runs on MTC_CT { + var BSC_ConnHdlr vc_conn1; + var BSC_ConnHdlr vc_conn2; + f_init(); + + f_vty_config(MSCVTY, "network", "authentication required"); + + var BSC_ConnHdlrPars pars1 := f_init_pars(92); + pars1.net.expect_auth := true; + var BSC_ConnHdlrPars pars2 := pars1; + + vc_conn1 := f_start_handler_create(pars1); + vc_conn2 := f_start_handler_create(pars2); + connect(vc_conn1:COORD, vc_conn2:COORD); + f_start_handler_run(vc_conn1, refers(f_tc_call_re_establishment_1), pars1); + f_start_handler_run(vc_conn2, refers(f_tc_call_re_establishment_2), pars2); + vc_conn1.done; + vc_conn2.done; +} + +testcase TC_call_re_establishment_ciph() runs on MTC_CT { + var BSC_ConnHdlr vc_conn1; + var BSC_ConnHdlr vc_conn2; + f_init(); + + f_vty_config(MSCVTY, "network", "authentication required"); + f_vty_config(MSCVTY, "network", "encryption a5 3"); + + var BSC_ConnHdlrPars pars1 := f_init_pars(92); + pars1.net.expect_auth := true; + pars1.net.expect_ciph := true; + pars1.net.kc_support := '08'O; /* A5/3 only */ + var BSC_ConnHdlrPars pars2 := pars1; + + vc_conn1 := f_start_handler_create(pars1); + vc_conn2 := f_start_handler_create(pars2); + connect(vc_conn1:COORD, vc_conn2:COORD); + f_start_handler_run(vc_conn1, refers(f_tc_call_re_establishment_1), pars1); + f_start_handler_run(vc_conn2, refers(f_tc_call_re_establishment_2), pars2); + vc_conn1.done; + vc_conn2.done; +} + +/* Establish a conn with a valid Mobile Identity. Then send a CM Service Request containing a mismatching Mobile + * Identity on the same conn. Caused a crash, see OS#5532. */ +friend function f_tc_cm_serv_wrong_mi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* Set up a fully identified conn */ + f_perform_lu(); + f_establish_fully(); + + /* CM Serv Req with mismatching Mobile Identity */ + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(f_gen_imsi(99999))); /* ensure it is different from below*/ + BSSAP.send(ts_PDU_DTAP_MO(ts_CM_SERV_REQ(CM_TYPE_MO_SMS, mi))); + BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ)); + + /* Cancel the first CM Service from f_establish_fully() */ + BSSAP.send(ts_BSSMAP_ClearRequest(0)); + + f_expect_clear(); +} +testcase TC_cm_serv_wrong_mi() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + vc_conn := f_start_handler(refers(f_tc_cm_serv_wrong_mi), 94); + vc_conn.done; +} + +/* a5 0 a5 0 a5 0 3 a5 0 3 a5 3 a5 3 + * HLR has auth info no yes no yes no yes + * + * test case index [0] [1] [2] [3] [4] [5] + * authentication optional No auth No auth attempt auth, auth reject auth + * (%) fall back to +ciph +ciph + * no-auth + * + * [6] [7] [8] [9] [10] [11] + * authentication mandatory reject auth reject auth reject auth + * only +ciph +ciph + * + * (%): Arguably, when HLR has auth info, the MSC should use it. Current behavior of osmo-msc is to not attempt auth at + * all. Related: OS#4830. + */ +type record of BSC_ConnHdlrNetworkPars rof_netpars; + +const rof_netpars auth_options_testcases := { + { + /* [0] auth optional, encr a5 0: no-auth" */ + kc_support := '01'O, + net_config := { "authentication optional", + "encryption a5 0" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := false, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [1] auth optional, encr a5 0, HLR HAS auth info: no-auth */ + kc_support := '01'O, + net_config := { "authentication optional", + "encryption a5 0" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := false, + hlr_has_auth_info := true, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [2] auth optional, encr a5 0 3, HLR has NO Auth Info: Fall back to no-auth" */ + kc_support := '09'O, + net_config := { "authentication optional", + "encryption a5 0 3" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [3] auth optional, encr a5 0 3, HLR HAS Auth Info: Use A5/3 */ + kc_support := '09'O, + net_config := { "authentication optional", + "encryption a5 0 3" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := true, + expect_auth := true, + expect_ciph := true, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [4] auth optional, encr a5 3, HLR has NO Auth Info: reject. + * Auth is required implicitly because ciph is required. */ + kc_support := '08'O, + net_config := { "authentication optional", + "encryption a5 3" }, + expect_attach_success := false, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [5] auth optional, encr a5 3, HLR HAS Auth Info: auth + ciph. + * Auth is required implicitly because ciph is required. */ + kc_support := '08'O, + net_config := { "authentication optional", + "encryption a5 3" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := true, + expect_auth := true, + expect_ciph := true, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + + /* Same as above, but with 'authentication required' */ + + { + /* [6] auth required, encr a5 0, HLR has NO auth info: reject */ + kc_support := '01'O, + net_config := { "authentication required", + "encryption a5 0" }, + expect_attach_success := false, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [7] auth required, encr a5 0, HLR HAS auth info: do auth, no ciph" */ + kc_support := '01'O, + net_config := { "authentication required", + "encryption a5 0" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := true, + expect_auth := true, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [8] auth required, encr a5 0 3, HLR has NO Auth Info: reject */ + kc_support := '09'O, + net_config := { "authentication required", + "encryption a5 0 3" }, + expect_attach_success := false, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [9] auth required, encr a5 0 3, HLR HAS Auth Info: Use A5/3 */ + kc_support := '09'O, + net_config := { "authentication required", + "encryption a5 0 3" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := true, + expect_auth := true, + expect_ciph := true, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [10] auth required, encr a5 3, HLR has NO Auth Info: reject. */ + kc_support := '08'O, + net_config := { "authentication required", + "encryption a5 3" }, + expect_attach_success := false, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := false, + expect_auth := false, + expect_ciph := false, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + }, + { + /* [11] auth required, encr a5 3, HLR HAS Auth Info: auth + ciph. */ + kc_support := '08'O, + net_config := { "authentication required", + "encryption a5 3" }, + expect_attach_success := true, + expect_tmsi := true, + expect_auth_attempt := true, + hlr_has_auth_info := true, + expect_auth := true, + expect_ciph := true, + expect_imei := false, + expect_imei_early := false, + check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK, + check_imei_error := false + } +}; + +private function f_tc_auth_options(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + /* Location Updating */ + log(MSCVTY, "f_perform_lu() starting"); + f_perform_lu(); + log(MSCVTY, "f_perform_lu() done"); + + f_sleep(1.0); + + if (not pars.net.expect_attach_success) { + /* Expected above LU to fail. In order to test CM Service Request below, a LU has to succeed first. So + * run another LU that will be successful. Careful not to load auth tokens into the VLR that may taint + * the test for CM Service Request below. */ + + log(MSCVTY, "Running a successful LU so that CM Service Request can be tested"); + var BSC_ConnHdlrNetworkPars saved_net := g_pars.net; + g_pars.net.kc_support := '01'O; + g_pars.net.expect_attach_success := true; + g_pars.net.expect_auth_attempt := false; + g_pars.net.expect_auth := false; + g_pars.net.expect_ciph := false; + f_vty_config3(MSCVTY, {"network"}, {"authentication optional", "encryption a5 0"}); + f_perform_lu(); + + /* Reconfigure like it was before */ + g_pars.net := saved_net; + f_vty_config3(MSCVTY, {"network"}, g_pars.net.net_config); + log(MSCVTY, "Running a successful LU done"); + } + + /* CM Service Request */ + log(MSCVTY, "f_establish_fully() starting"); + f_establish_fully(); + log(MSCVTY, "f_establish_fully() done"); + BSSAP.send(ts_BSSMAP_ClearRequest(0)); + f_expect_clear(); +} + +function f_TC_auth_options(integer tc_i) runs on MTC_CT { + f_init(); + + var BSC_ConnHdlrNetworkPars tc := auth_options_testcases[tc_i]; + + f_vty_config3(MSCVTY, {"network"}, tc.net_config); + + var BSC_ConnHdlrPars pars := f_init_pars(42300 + tc_i); + pars.net := tc; + + var BSC_ConnHdlr vc_conn; + vc_conn := f_start_handler_with_pars(refers(f_tc_auth_options), pars); + vc_conn.done; +} + +testcase TC_auth_options_0() runs on MTC_CT { + f_TC_auth_options(0); +} + +testcase TC_auth_options_1() runs on MTC_CT { + f_TC_auth_options(1); +} + +testcase TC_auth_options_2() runs on MTC_CT { + f_TC_auth_options(2); +} + +testcase TC_auth_options_3() runs on MTC_CT { + f_TC_auth_options(3); +} + +testcase TC_auth_options_4() runs on MTC_CT { + f_TC_auth_options(4); +} + +testcase TC_auth_options_5() runs on MTC_CT { + f_TC_auth_options(5); +} + +testcase TC_auth_options_6() runs on MTC_CT { + f_TC_auth_options(6); +} + +testcase TC_auth_options_7() runs on MTC_CT { + f_TC_auth_options(7); +} + +testcase TC_auth_options_8() runs on MTC_CT { + f_TC_auth_options(8); +} + +testcase TC_auth_options_9() runs on MTC_CT { + f_TC_auth_options(9); +} + +testcase TC_auth_options_10() runs on MTC_CT { + f_TC_auth_options(10); +} + +testcase TC_auth_options_11() runs on MTC_CT { + f_TC_auth_options(11); +} + +private function f_set_cpars_csd(inout CallParameters cpars, charstring bs_name, BIT1 async, + GSM48_bcap_transp transp, GSM48_bcap_user_rate user_rate) { + log("-----------------------------------------------"); + log("CSD Bearer Service: " & bs_name); + log("-----------------------------------------------"); + + cpars.csd := true; + + cpars.bearer_cap := valueof(ts_Bcap_csd); + cpars.bearer_cap.octet6.synchronous_asynchronous := async; + cpars.bearer_cap.octet6.connectionElement := int2bit(enum2int(transp), 2); + cpars.bearer_cap.octet6.userRate := int2bit(enum2int(user_rate), 4); + + cpars.mncc_bearer_cap := valueof(ts_MNCC_bcap_data); + cpars.mncc_bearer_cap.data.async := bit2int(async); + cpars.mncc_bearer_cap.data.transp := transp; + cpars.mncc_bearer_cap.data.user_rate := user_rate; +} + +friend function f_mo_csd(charstring bs_name, BIT1 async, GSM48_bcap_transp transp, GSM48_bcap_user_rate user_rate) + runs on BSC_ConnHdlr { + var CallParameters cpars := valueof(t_CallParams); + + g_Tguard.start(20.0); + f_set_cpars_csd(cpars, bs_name, async, transp, user_rate); + f_perform_lu(); + f_mo_call(cpars, 0.5); +} + +friend function f_tc_lu_and_mo_csd(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + f_mo_csd("BS21T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_300); + f_mo_csd("BS22T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_1200); + f_mo_csd("BS24T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_2400); + f_mo_csd("BS25T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); + f_mo_csd("BS26T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_9600); + + f_mo_csd("BS21NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_300); + f_mo_csd("BS22NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_1200); + f_mo_csd("BS24NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_2400); + f_mo_csd("BS25NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_4800); + f_mo_csd("BS26NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_9600); + + f_mo_csd("BS31T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_1200); + f_mo_csd("BS32T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_2400); + f_mo_csd("BS33T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); + f_mo_csd("BS34T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_9600); +} +testcase TC_lu_and_mo_csd() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_and_mo_csd), 7); + vc_conn.done; +} + +friend function f_mt_csd(charstring bs_name, BIT1 async, GSM48_bcap_transp transp, GSM48_bcap_user_rate user_rate) + runs on BSC_ConnHdlr { + var CallParameters cpars := valueof(t_CallParams); + + g_Tguard.start(20.0); + f_set_cpars_csd(cpars, bs_name, async, transp, user_rate); + f_perform_lu(); + f_mt_call(cpars, 0.5); +} +friend function f_tc_lu_and_mt_csd(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + + f_mt_csd("BS21T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_300); + f_mt_csd("BS22T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_1200); + f_mt_csd("BS24T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_2400); + f_mt_csd("BS25T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); + f_mt_csd("BS26T", '1'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_9600); + + f_mt_csd("BS21NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_300); + f_mt_csd("BS22NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_1200); + f_mt_csd("BS24NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_2400); + f_mt_csd("BS25NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_4800); + f_mt_csd("BS26NT", '1'B, GSM48_BCAP_TR_RLP, GSM48_BCAP_UR_9600); + + f_mt_csd("BS31T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_1200); + f_mt_csd("BS32T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_2400); + f_mt_csd("BS33T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_4800); + f_mt_csd("BS34T", '0'B, GSM48_BCAP_TR_TRANSP, GSM48_BCAP_UR_9600); +} +testcase TC_lu_and_mt_csd() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_and_mt_csd), 7); + vc_conn.done; +} + +/* MSC <-> BSC: ID req/rsp for IMSI */ +private altstep as_id_req_imsi() +runs on BSC_ConnHdlr { + [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req(CM_ID_TYPE_IMSI))) { + var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_MM_ID_Rsp(mi))); + repeat; + } +} + +/* MSC is configured to not assign a TMSI; MS sends LU Request with a TMSI MI (from another cell), and MSC shall not use + * that TMSI. */ +private function f_tc_lu_tmsi_noauth_notmsi(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars, t_guard := 20.0); + + /* Perform Location Updating using an unknown TMSI MI. Expect an ID Request to come from the MSC and answer + * that with as_id_req_imsi(). */ + activate(as_id_req_imsi()); + f_perform_lu(use_mi := ts_MI_TMSI_LV(pars.tmsi)); + + f_sleep(1.0); + + /* Attached by invalid TMSI, and the MSC has asked for the IMSI. Initiate Paging and make sure the MSC doesn't + * use the invalid TMSI for it. */ + f_ran_register_imsi(g_pars.imsi, omit); + f_vty_transceive(MSCVTY, "subscriber imsi " & hex2str(g_pars.imsi) & " paging"); + f_expect_paging_tmsi(omit); + + /* Respond to paging, to clean up internal paging state for this subscriber, so we can get a clean second run + * out of this test code. Don't use the TMSI in the paging response. */ + f_cl3_or_initial_ue(valueof(ts_PAG_RESP(ts_MI_IMSI_LV(pars.imsi)))); + f_mm_common(); + /* The paging was by VTY, so nothing happens, just a release. */ + f_expect_clear(); + + /* Clean up ttcn state for the second test run to work out. */ + f_unregister_gsup_imsi(hex2str(pars.imsi)); + f_ran_unregister_imsi(pars.imsi); +} +testcase TC_lu_tmsi_noauth_notmsi() runs on MTC_CT { + var BSC_ConnHdlrPars pars; + var BSC_ConnHdlr vc_conn; + f_init(); + f_vty_config(MSCVTY, "msc", "no assign-tmsi"); + pars := f_init_pars(101); + pars.net.expect_tmsi := false; + pars.tmsi := '0badbad0'O; + pars.mm_info := false; + vc_conn := f_start_handler_with_pars(refers(f_tc_lu_tmsi_noauth_notmsi), pars); + vc_conn.done; + + /* Now run the same test *again*, to test against an evil twin VLR entry: + * A vlr_subscr with the correct IMSI is now present in the VLR. + * We again ask for a LU with the 0x0bad TMSI. The VLR will initially create another vlr_subsrc(TMSI=0x0bad). + * When it learns the IMSI via ID Request, it needs to realize that this IMSI is already present on the first + * vsub, and sort out the VLR record so that only one entry for this IMSI exists. + */ + pars := f_init_pars(101); + pars.net.expect_tmsi := false; + pars.tmsi := '0badbad0'O; + pars.mm_info := false; + vc_conn := f_start_handler_with_pars(refers(f_tc_lu_tmsi_noauth_notmsi), pars); + vc_conn.done; +} + +friend function f_tc_lu_and_mo_call_reass_for_mt_codec(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams); + /* MO will first choose GSM-FR = BSC returns GSM-FR in Assignment Complete's Codec (Chosen) */ + cpars.ass_compl_chosen_codec := valueof(ts_CodecFR); + /* But when the MT call leg responds, we learn that it only supports AMR */ + cpars.mncc_alert_req_sends_remote_sdp := + "v=0\r\n" + & "o=BSC_ConnectionHandler.ttcn 0 0 IN IP4 1.1.1.1\r\n" + & "s=GSM Call\r\n" + & "c=IN IP4 " & cpars.bss_rtp_ip & "\r\n" + & "t=0 0\r\n" + & "m=audio " & int2str(cpars.bss_rtp_port) & " RTP/AVP 112\r\n" + & "a=rtpmap:112 AMR/8000\r\n" + & "a=fmtp:112 octet-align=1\r\n" + & "a=ptime:20\r\n"; + /* MSC should ask for another Assignment after MNCC_ALERT_REQ, that should now contain only the remote call + * leg's capabilities, i.e. only AMR. */ + cpars.expect_re_assignment := true; + cpars.re_ass_req_codecs := {valueof(ts_CodecAMR_F), valueof(ts_CodecAMR_H)}; + /* When the MSC asks for another Assignment after MNCC_ALERT_REQ, we'll give it AMR. */ + cpars.re_ass_compl_chosen_codec := valueof(ts_CodecAMR_F); + f_perform_lu(); + f_mo_call(cpars); +} +testcase TC_lu_and_mo_call_reass_for_mt_codec() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_lu_and_mo_call_reass_for_mt_codec), 7); + vc_conn.done; +} + control { execute( TC_cr_before_reset() ); execute( TC_lu_imsi_noauth_tmsi() ); execute( TC_lu_imsi_noauth_notmsi() ); + execute( TC_lu_tmsi_noauth_notmsi() ); execute( TC_lu_imsi_reject() ); execute( TC_lu_imsi_timeout_gsup() ); execute( TC_lu_imsi_auth_tmsi() ); @@ -6319,6 +7465,8 @@ control { execute( TC_lu_auth_sai_timeout() ); execute( TC_lu_auth_sai_err() ); execute( TC_lu_clear_request() ); + execute( TC_mo_call_clear_request() ); + execute( TC_mt_call_clear_request() ); execute( TC_lu_disconnect() ); execute( TC_lu_by_imei() ); execute( TC_lu_by_tmsi_noauth_unknown() ); @@ -6342,7 +7490,7 @@ control { execute( TC_mo_crcx_ran_reject() ); execute( TC_mt_crcx_ran_reject() ); execute( TC_mo_setup_and_dtmf_dup() ); - //execute( TC_mt_t310() ); + execute( TC_mt_t310() ); execute( TC_gsup_cancel() ); execute( TC_lu_imsi_auth_tmsi_encr_1_13() ); execute( TC_lu_imsi_auth_tmsi_encr_3_13() ); @@ -6350,6 +7498,10 @@ control { execute( TC_lu_imsi_auth_tmsi_encr_3_1_no_cm() ); execute( TC_lu_imsi_auth_tmsi_encr_13_2() ); execute( TC_lu_imsi_auth_tmsi_encr_013_2() ); + execute( TC_lu_imsi_auth_tmsi_encr_0134_1() ); + execute( TC_lu_imsi_auth_tmsi_encr_0134_34() ); + execute( TC_lu_imsi_auth_tmsi_encr_0134_34_no_cm3() ); + execute( TC_mo_release_timeout() ); execute( TC_lu_and_mt_call_no_dlcx_resp() ); execute( TC_reset_two() ); @@ -6375,6 +7527,7 @@ control { execute( TC_gsup_mt_sms_rp_mr() ); execute( TC_gsup_mo_mt_sms_rp_mr() ); execute( TC_gsup_mt_multi_part_sms() ); + execute( TC_gsup_mt_sms_lu_delay() ); execute( TC_lu_and_mo_ussd_single_request() ); execute( TC_lu_and_mt_ussd_notification() ); @@ -6423,10 +7576,18 @@ control { execute( TC_ho_inter_bsc_unknown_cell() ); execute( TC_ho_inter_bsc() ); + execute( TC_ho_inter_bsc_a5_1() ); + execute( TC_ho_inter_bsc_a5_3() ); + execute( TC_ho_inter_bsc_a5_4() ); execute( TC_ho_inter_bsc_ipv6() ); + execute( TC_ho_inter_bsc_csd() ); execute( TC_ho_inter_msc_out() ); + execute( TC_ho_inter_msc_out_a5_1() ); + execute( TC_ho_inter_msc_out_a5_3() ); + execute( TC_ho_inter_msc_out_a5_4() ); execute( TC_ho_inter_msc_out_ipv6() ); + execute( TC_ho_inter_msc_out_csd() ); execute( TC_lu_imsi_auth_tmsi_check_imei() ); execute( TC_lu_imsi_auth3g_tmsi_check_imei() ); @@ -6446,10 +7607,32 @@ control { execute( TC_invalid_mgcp_crash() ); execute( TC_mm_id_resp_no_identity() ); execute( TC_lu_and_expire_while_paging() ); - if (mp_enable_crashing_tests) { - execute( TC_paging_response_imsi_unknown() ); - execute( TC_paging_response_tmsi_unknown() ); - } + execute( TC_paging_response_imsi_unknown() ); + execute( TC_paging_response_tmsi_unknown() ); + + execute( TC_call_re_establishment() ); + execute( TC_call_re_establishment_auth() ); + execute( TC_call_re_establishment_ciph() ); + + execute( TC_cm_serv_wrong_mi() ); + + execute( TC_auth_options_0() ); + execute( TC_auth_options_1() ); + execute( TC_auth_options_2() ); + execute( TC_auth_options_3() ); + execute( TC_auth_options_4() ); + execute( TC_auth_options_5() ); + execute( TC_auth_options_6() ); + execute( TC_auth_options_7() ); + execute( TC_auth_options_8() ); + execute( TC_auth_options_9() ); + execute( TC_auth_options_10() ); + execute( TC_auth_options_11() ); + + execute( TC_lu_and_mo_csd() ); + execute( TC_lu_and_mt_csd() ); + + execute( TC_lu_and_mo_call_reass_for_mt_codec() ); } |