module BSC_Tests { /* Integration Tests for OsmoBSC * (C) 2017-2018 by Harald Welte * All rights reserved. * * Released under the terms of GNU General Public License, Version 2 or * (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * This test suite tests OsmoBSC while emulating both multiple BTS + MS as * well as the MSC. See README for more details. * * There are test cases that run in so-called 'handler mode' and test cases * that run directly on top of the BSSAP and RSL CodecPorts. The "handler mode" * tests abstract the multiplexing/demultiplexing of multiple SCCP connections * and/or RSL channels and are hence suitable for higher-level test cases, while * the "raw" tests directly on top of the CodecPorts are more suitable for lower- * level testing. */ import from Misc_Helpers all; import from General_Types all; import from Osmocom_Types all; import from GSM_Types all; import from IPL4asp_Types all; import from BSSAP_Types all; import from RAN_Adapter all; import from BSSAP_LE_Adapter all; import from BSSAP_LE_CodecPort all; import from BSSAP_LE_Types all; import from BSSLAP_Types all; import from BSSAP_CodecPort all; import from BSSMAP_Templates all; import from IPA_Emulation all; import from IPA_CodecPort all; import from IPA_Types all; import from IPA_Testing all; import from RSL_Types all; import from RSL_Emulation all; import from MGCP_Emulation all; import from MGCP_Templates all; import from MGCP_Types all; import from MGCP_CodecPort all; import from Osmocom_CTRL_Functions all; import from Osmocom_CTRL_Types all; import from Osmocom_CTRL_Adapter all; import from StatsD_Types all; import from StatsD_CodecPort all; import from StatsD_CodecPort_CtrlFunct all; import from StatsD_Checker all; import from Osmocom_VTY_Functions all; import from TELNETasp_PortType all; import from MobileL3_CommonIE_Types all; import from MobileL3_Types all; import from MobileL3_RRM_Types all; import from L3_Templates all; import from GSM_RR_Types all; import from SCCP_Templates all; import from BSSMAP_Templates all; import from BSSMAP_LE_Templates all; import from SCCPasp_Types all; import from GSM_SystemInformation all; import from GSM_RestOctets all; import from TCCConversion_Functions all; const integer NUM_BTS := 3; const integer NUM_MSC := 3; const float T3101_MAX := 12.0; /* make sure to sync this with the osmo-bts.cfg you're using */ const integer NUM_TCHH_PER_BTS := 2; const integer NUM_TCHF_PER_BTS := 4; const integer NUM_SDCCH_PER_BTS := 3; /* per-BTS state which we keep */ type record BTS_State { /* component reference to the IPA_Client component used for RSL */ IPA_Client rsl } /* Default list of counters for an 'msc' entity. */ const CounterNameVals counternames_msc_mscpool := { { "mscpool:subscr:new", 0 }, { "mscpool:subscr:known", 0 }, { "mscpool:subscr:reattach", 0 }, { "mscpool:subscr:attach_lost", 0 }, { "mscpool:subscr:paged", 0 } }; /* Default list of counters for 'bsc' and 'bts' entities. */ const CounterNameVals counternames_bsc_bts_handover := { { "assignment:attempted", 0 }, { "assignment:completed", 0 }, { "assignment:stopped", 0 }, { "assignment:no_channel", 0 }, { "assignment:timeout", 0 }, { "assignment:failed", 0 }, { "assignment:error", 0 }, { "handover:attempted", 0 }, { "handover:completed", 0 }, { "handover:stopped", 0 }, { "handover:no_channel", 0 }, { "handover:timeout", 0 }, { "handover:failed", 0 }, { "handover:error", 0 }, { "intra_cell_ho:attempted", 0 }, { "intra_cell_ho:completed", 0 }, { "intra_cell_ho:stopped", 0 }, { "intra_cell_ho:no_channel", 0 }, { "intra_cell_ho:timeout", 0 }, { "intra_cell_ho:failed", 0 }, { "intra_cell_ho:error", 0 }, { "intra_bsc_ho:attempted", 0 }, { "intra_bsc_ho:completed", 0 }, { "intra_bsc_ho:stopped", 0 }, { "intra_bsc_ho:no_channel", 0 }, { "intra_bsc_ho:timeout", 0 }, { "intra_bsc_ho:failed", 0 }, { "intra_bsc_ho:error", 0 }, { "interbsc_ho_out:attempted", 0 }, { "interbsc_ho_out:completed", 0 }, { "interbsc_ho_out:stopped", 0 }, { "interbsc_ho_out:timeout", 0 }, { "interbsc_ho_out:failed", 0 }, { "interbsc_ho_out:error", 0 }, { "interbsc_ho_in:attempted", 0 }, { "interbsc_ho_in:completed", 0 }, { "interbsc_ho_in:stopped", 0 }, { "interbsc_ho_in:no_channel", 0 }, { "interbsc_ho_in:timeout", 0 }, { "interbsc_ho_in:failed", 0 }, { "interbsc_ho_in:error", 0 } }; /* Set of all System Information received during one RSL port's startup. * Note that some System Information may be sent on RSL, but lacking actual SI data, to indicate that the BTS should not * broadcast that SI type. That will be reflected as 'omit' here. */ type record SystemInformationConfig { SystemInformationType1 si1 optional, SystemInformationType2 si2 optional, SystemInformationType2bis si2bis optional, SystemInformationType2ter si2ter optional, SI2quaterRestOctetsList si2quater optional, SystemInformationType3 si3 optional, SystemInformationType4 si4 optional, /* TODO: replace with proper decoding of SI13, implement SI13 in GSM_SystemInformation.ttcn */ octetstring si13 optional, SystemInformationType5 si5 optional, SystemInformationType5bis si5bis optional, SystemInformationType5ter si5ter optional, SystemInformationType6 si6 optional }; const SystemInformationConfig SystemInformationConfig_omit := { si1 := omit, si2 := omit, si2bis := omit, si2ter := omit, si2quater := omit, si3 := omit, si4 := omit, si13 := omit, si5 := omit, si5bis := omit, si5ter := omit, si6 := omit }; /* tr_EUTRAN_CellDesc with defaults used in BSC_Tests.ttcn */ template EUTRAN_CellDesc tr_EUTRAN_CellDesc_default(template (present) uint16_t e_arfcn := ?, template uint3_t meas_bw := 3) := tr_EUTRAN_CellDesc(e_arfcn := e_arfcn, meas_bw_presence := '1'B, meas_bw := meas_bw); /* tr_EUTRAN_NeighbourCells with defaults used in BSC_Tests.ttcn */ template EUTRAN_NeighbourCells tr_EUTRAN_NeighbourCells_default(template (present) EUTRAN_CellDescs cell_desc_list := { tr_EUTRAN_CellDesc_default }, template uint3_t prio := 3, template (present) uint5_t thresh_high := 20, template uint5_t thresh_low := 10, template uint5_t qrxlevmin := 22) := tr_EUTRAN_NeighbourCells( cell_desc_list := cell_desc_list, prio_presence := '1'B, prio := prio, thresh_high := thresh_high, thresh_low_presence := '1'B, thresh_low := thresh_low, qrxlevmin_presence := '1'B, qrxlevmin := qrxlevmin); template SystemInformationConfig SystemInformationConfig_default := { si1 := { cell_chan_desc := '8FB38000000000000000000000000000'O, rach_control := { max_retrans := RACH_MAX_RETRANS_7, tx_integer := '1001'B, cell_barr_access := false, re_not_allowed := true, acc := '0000010000000000'B }, rest_octets := ? }, si2 := { bcch_freq_list := '00000000000000000000000000000000'O, ncc_permitted := '11111111'B, rach_control := { max_retrans := RACH_MAX_RETRANS_7, tx_integer := '1001'B, cell_barr_access := false, re_not_allowed := true, acc := '0000010000000000'B } }, si2bis := omit, si2ter := { extd_bcch_freq_list := '8E320000000000000000000000000800'O, rest_octets := ? }, si2quater := { tr_SI2quaterRestOctets_EUTRAN( repeated_neigh_cells := { tr_EUTRAN_NeighbourCells_default } ) }, si3 := { cell_id := 0, lai := { mcc_mnc := '001F01'H, lac := 1 }, ctrl_chan_desc := { msc_r99 := true, att := true, bs_ag_blks_res := 1, ccch_conf := CCHAN_DESC_1CCCH_COMBINED, si22ind := false, cbq3 := CBQ3_IU_MODE_NOT_SUPPORTED, spare := '00'B, bs_pa_mfrms := 3, t3212 := 30 }, cell_options := { dn_ind := false, pwrc := false, dtx := MS_SHALL_USE_UL_DTX, radio_link_tout_div4 := 7 }, cell_sel_par := { cell_resel_hyst_2dB := 2, ms_txpwr_max_cch := 7, acs := '0'B, neci := true, rxlev_access_min := 0 }, rach_control := { max_retrans := RACH_MAX_RETRANS_7, tx_integer := '1001'B, cell_barr_access := false, re_not_allowed := true, acc := '0000010000000000'B }, rest_octets := { sel_params := { presence := '0'B, params := omit }, pwr_offset := { presence := '0'B, offset := omit }, si_2ter_ind := '1'B, early_cm_ind := '0'B, sched_where := { presence := '0'B, where := omit }, gprs_ind := { presence := '1'B, ind := { ra_colour := 0, si13_pos := '0'B } }, umts_early_cm_ind := '1'B, si2_quater_ind := { presence := '1'B, ind := '0'B }, iu_mode_ind := omit, si21_ind := { presence := '0'B, pos := omit } } }, si4 := { lai := { mcc_mnc := '001F01'H, lac := 1 }, cell_sel_par := { cell_resel_hyst_2dB := 2, ms_txpwr_max_cch := 7, acs := '0'B, neci := true, rxlev_access_min := 0 }, rach_control := { max_retrans := RACH_MAX_RETRANS_7, tx_integer := '1001'B, cell_barr_access := false, re_not_allowed := true, acc := '0000010000000000'B }, cbch_chan_desc := { iei := '64'O, v := { chan_nr := { u := { sdcch4 := { tag := '001'B, sub_chan := 2 } }, tn := 0 }, tsc := 2, h := false, arfcn := 871, maio_hsn := omit } }, cbch_mobile_alloc := omit, rest_octets := { sel_params := { presence := '0'B, params := omit }, pwr_offset := { presence := '0'B, offset := omit }, gprs_ind := { presence := '1'B, ind := { ra_colour := 0, si13_pos := '0'B } }, s_presence := '0'B, s := omit } }, si13 := '9000185A6FC9E08410AB2B2B2B2B2B2B2B2B2B2B'O, si5 := { bcch_freq_list := '10000000000000000000000000000000'O }, si5bis := omit, si5ter := { extd_bcch_freq_list := '9E050020000000000000000000000000'O }, si6 := { cell_id := 0, lai := { mcc_mnc := '001F01'H, lac := 1 }, cell_options := { dtx_ext := '1'B, pwrc := false, dtx := '01'B, radio_link_timeout := '0111'B }, ncc_permitted := '11111111'B, rest_octets := ? } }; /* List of all the System Information received on all RSL ports */ type record of SystemInformationConfig SystemInformationConfig_list; function f_sysinfo_dec_raw(inout SystemInformationConfig si, RSL_Message rsl) { var RSL_IE_Body sysinfo_type_ie; var RSL_IE_SysinfoType si_type; var octetstring data; if (f_rsl_find_ie(rsl, RSL_IE_SYSINFO_TYPE, sysinfo_type_ie) == false) { setverdict(fail, "Cannot find RSL_IE_SYSINFO_TYPE"); mtc.stop; } si_type := sysinfo_type_ie.sysinfo_type; if (rsl.msg_type == RSL_MT_BCCH_INFO) { var RSL_IE_Body bcch_ie; if (f_rsl_find_ie(rsl, RSL_IE_FULL_BCCH_INFO, bcch_ie)) { data := bcch_ie.other.payload; } } else if (rsl.msg_type == RSL_MT_SACCH_FILL) { var RSL_IE_Body l3_ie; if (f_rsl_find_ie(rsl, RSL_IE_L3_INFO, l3_ie)) { data := l3_ie.l3_info.payload; } } else { setverdict(fail, "Don't understand this System Information message"); mtc.stop; } var boolean handled := false; if (rsl.msg_type == RSL_MT_BCCH_INFO) { handled := true; if (si_type == RSL_SYSTEM_INFO_1) { if (not isbound(data)) { si.si1 := omit; } else { si.si1 := dec_SystemInformation(data).payload.si1; } } else if (si_type == RSL_SYSTEM_INFO_2) { if (not isbound(data)) { si.si2 := omit; } else { si.si2 := dec_SystemInformation(data).payload.si2; } } else if (si_type == RSL_SYSTEM_INFO_2bis) { if (not isbound(data)) { si.si2bis := omit; } else { si.si2bis := dec_SystemInformation(data).payload.si2bis; } } else if (si_type == RSL_SYSTEM_INFO_2ter) { if (not isbound(data)) { si.si2ter := omit; } else { si.si2ter := dec_SystemInformation(data).payload.si2ter; } } else if (si_type == RSL_SYSTEM_INFO_2quater) { if (not isbound(data)) { si.si2quater := {}; } else { var SystemInformationType2quater decoded := dec_SystemInformation(data).payload.si2quater; /* this is a *record* of SI2quaterRestOctets! (multiplexed) */ si.si2quater[decoded.rest_octets.si2quater_index] := decoded.rest_octets; } } else if (si_type == RSL_SYSTEM_INFO_3) { if (not isbound(data)) { si.si3 := omit; } else { si.si3 := dec_SystemInformation(data).payload.si3; } } else if (si_type == RSL_SYSTEM_INFO_4) { if (not isbound(data)) { si.si4 := omit; } else { si.si4 := dec_SystemInformation(data).payload.si4; } } else if (si_type == RSL_SYSTEM_INFO_13) { if (not isbound(data)) { si.si13 := omit; } else { si.si13 := dec_SystemInformation(data).payload.other; } } else { handled := false; } } else if (rsl.msg_type == RSL_MT_SACCH_FILL) { handled := true; if (si_type == RSL_SYSTEM_INFO_5) { if (not isbound(data)) { si.si5 := omit; } else { si.si5 := dec_SystemInformation(data).payload.si5; } } else if (si_type == RSL_SYSTEM_INFO_5bis) { if (not isbound(data)) { si.si5bis := omit; } else { si.si5bis := dec_SystemInformation(data).payload.si5bis; } } else if (si_type == RSL_SYSTEM_INFO_5ter) { if (not isbound(data)) { si.si5ter := omit; } else { si.si5ter := dec_SystemInformation(data).payload.si5ter; } } else if (si_type == RSL_SYSTEM_INFO_6) { if (not isbound(data)) { si.si6 := omit; } else { si.si6 := dec_SystemInformation(data).payload.si6; } } else { handled := false; } } if (not handled) { setverdict(fail, "Unexpected SI type in ", rsl.msg_type, " message: ", si_type); } } type component test_CT extends CTRL_Adapter_CT { /* Array of per-BTS state */ var BTS_State bts[NUM_BTS]; /* RSL common Channel Port (for RSL_Emulation) */ port RSL_CCHAN_PT RSL_CCHAN[NUM_BTS]; /* array of per-BTS RSL test ports */ port IPA_RSL_PT IPA_RSL[NUM_BTS]; port IPA_CODEC_PT IPA; /* Required for compilation of TC_rsl_unknown_unit_id() */ /* CTRL muxed over IPA in SCCPlite conn BSC<->MSC (or BSC-NAT) */ port IPA_CTRL_PT SCCPLITE_IPA_CTRL; var MGCP_Emulation_CT vc_MGCP; port TELNETasp_PT BSCVTY; /* StatsD */ var StatsD_Checker_CT vc_STATSD; var RAN_Adapter g_bssap[NUM_MSC]; var BSSAP_LE_Adapter g_bssap_le; /* for old legacy-tests only */ port BSSAP_CODEC_PT BSSAP; port BSSAP_LE_CODEC_PT BSSAP_LE; /* are we initialized yet */ var boolean g_initialized := false; /* Osmux is enabled through VTY */ var boolean g_osmux_enabled := false; /*Configure T(tias) over VTY, seconds */ var integer g_bsc_sccp_timer_ias := 7 * 60; /*Configure T(tiar) over VTY, seconds */ var integer g_bsc_sccp_timer_iar := 15 * 60; /* global test case guard timer (actual timeout value is set in f_init()) */ timer T_guard := 30.0; var CounterNameValsList g_ctr_msc; var CounterNameValsList g_ctr_bsc; var CounterNameValsList g_ctr_bts; /* System Information bytes as received during RSL startup, for each RSL[idx]. */ var SystemInformationConfig_list g_system_information := {}; } modulepar { /* IP address at which the BSC can be reached */ charstring mp_bsc_ip := "127.0.0.1"; /* port number to which to establish the IPA OML connections */ integer mp_bsc_oml_port := 3002; /* port number to which to establish the IPA RSL connections */ integer mp_bsc_rsl_port := 3003; /* port number to which to establish the IPA CTRL connection */ integer mp_bsc_ctrl_port := 4249; /* port number to which to listen for STATSD metrics */ integer mp_bsc_statsd_port := 8125; /* IP address at which the test binds */ charstring mp_test_ip := "127.0.0.1"; RAN_Configurations mp_bssap_cfg := { { transport := BSSAP_TRANSPORT_AoIP, sccp_service_type := "mtp3_itu", sctp_addr := { 23905, "127.0.0.1", 2905, "127.0.0.1" }, own_pc := 185, /* 0.23.1 first MSC emulation */ own_ssn := 254, peer_pc := 187, /* 0.23.3 osmo-bsc */ peer_ssn := 254, sio := '83'O, rctx := 1 }, { transport := BSSAP_TRANSPORT_AoIP, sccp_service_type := "mtp3_itu", sctp_addr := { 23906, "127.0.0.1", 2905, "127.0.0.1" }, own_pc := 2, /* 0.0.2 second MSC emulation */ own_ssn := 254, peer_pc := 187, /* 0.23.3 osmo-bsc */ peer_ssn := 254, sio := '83'O, rctx := 2 }, { transport := BSSAP_TRANSPORT_AoIP, sccp_service_type := "mtp3_itu", sctp_addr := { 23907, "127.0.0.1", 2905, "127.0.0.1" }, own_pc := 3, /* 0.0.3 third MSC emulation */ own_ssn := 254, peer_pc := 187, /* 0.23.3 osmo-bsc */ peer_ssn := 254, sio := '83'O, rctx := 3 } }; BSSAP_LE_Configuration mp_bssap_le_cfg := { sccp_service_type := "mtp3_itu", sctp_addr := { 23908, "127.0.0.1", 2905, "127.0.0.1" }, own_pc := 190, /* 0.23.6 SMLC emulation */ own_ssn := 252, /* SMLC side SSN */ peer_pc := 187, /* 0.23.3 osmo-bsc */ peer_ssn := 250, /* BSC side SSN */ sio := '83'O, rctx := 6 }; boolean mp_enable_lcs_tests := true; /* Whether to enable osmux tests. Can be dropped completely and enable unconditionally once new version of osmo-bsc is released (current version: 1.4.1) */ boolean mp_enable_osmux_test := true; /* Value set in osmo-bsc.cfg "ms max power" */ uint8_t mp_exp_ms_power_level := 7; boolean mp_media_mgw_offer_ipv6 := true; } private function f_gen_test_hdlr_pars(integer bssap_idx := 0) return TestHdlrParams { var TestHdlrParams pars := valueof(t_def_TestHdlrPars); if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) { pars.aoip := true; } else { pars.aoip := false; } pars.exp_ms_power_level := mp_exp_ms_power_level; pars.mscpool.bssap_idx := bssap_idx; pars.media_mgw_offer_ipv6 := mp_media_mgw_offer_ipv6; return pars; } /* Convenience functions for rate counters using g_ctr_msc. */ private function f_ctrs_msc_init(integer mscs_count := NUM_MSC, CounterNameVals counternames := counternames_msc_mscpool) runs on test_CT { g_ctr_msc := f_counter_name_vals_get_n(IPA_CTRL, "msc", mscs_count, counternames); log("initial msc rate counters: ", g_ctr_msc); } private function f_ctrs_msc_add(integer msc_nr, charstring countername, integer val := 1) runs on test_CT { f_counter_name_vals_list_add(g_ctr_msc, msc_nr, countername, val); } /* f_ctrs_msc_init(); * f_do_thing(on_msc := 0); * f_do_thing(on_msc := 0); * f_do_other(on_msc := 1); * f_ctrs_msc_add(0, "thing", 2); * f_ctrs_msc_add(1, "other"); * f_ctrs_msc_verify(); */ private function f_ctrs_msc_verify() runs on test_CT { log("verifying msc rate counters: ", g_ctr_msc); f_counter_name_vals_expect_n(IPA_CTRL, "msc", g_ctr_msc); } /* convenience: f_ctrs_msc_add() and f_ctrs_msc_verify() in one call. * f_ctrs_msc_init(); * f_do_thing(on_msc := 0); * f_do_thing(on_msc := 0); * f_do_thing(on_msc := 0); * f_ctrs_msc_expect(0, "thing", 3); */ private function f_ctrs_msc_expect(integer msc_nr, charstring countername, integer val := 1) runs on test_CT { f_ctrs_msc_add(msc_nr, countername, val); f_ctrs_msc_verify(); } /* Convenience functions for rate counters using g_ctr_bts, always also including g_ctr_bsc. */ private function f_ctrs_bsc_and_bts_init(integer bts_count := NUM_BTS, CounterNameVals counternames := counternames_bsc_bts_handover) runs on test_CT { g_ctr_bts := f_counter_name_vals_get_n(IPA_CTRL, "bts", bts_count, counternames); log("initial bts rate counters: ", g_ctr_bts); f_ctrs_bsc_init(counternames); } private function f_ctrs_bsc_and_bts_add(integer bts_nr, charstring countername, integer val := 1) runs on test_CT { f_counter_name_vals_list_add(g_ctr_bts, bts_nr, countername, val); f_ctrs_bsc_add(countername, val); } /* f_ctrs_bsc_and_bts_init(); * f_do_thing(on_bts := 0); * f_do_thing(on_bts := 0); * f_do_other(on_bts := 1); * f_ctrs_bsc_and_bts_add(0, "thing", 2); * f_ctrs_bsc_and_bts_add(1, "other"); * f_ctrs_bsc_and_bts_verify(); */ private function f_ctrs_bsc_and_bts_verify() runs on test_CT { f_counter_name_vals_expect_n(IPA_CTRL, "bts", g_ctr_bts); f_ctrs_bsc_verify(); } /* convenience: f_ctrs_bsc_and_bts_add() and f_ctrs_bsc_and_bts_verify() in one call. * f_ctrs_bsc_and_bts_init(); * f_do_thing(on_bts := 0); * f_do_thing(on_bts := 0); * f_do_thing(on_bts := 0); * f_ctrs_bsc_and_bts_expect(0, "thing", 3); */ private function f_ctrs_bsc_and_bts_expect(integer bts_nr, charstring countername, integer val := 1) runs on test_CT { f_ctrs_bsc_and_bts_add(bts_nr, countername, val); f_ctrs_bsc_and_bts_verify(); } /* Convenience functions for rate counters using g_ctr_bsc. */ private function f_ctrs_bsc_init(CounterNameVals counternames := counternames_bsc_bts_handover) runs on test_CT { g_ctr_bsc := f_counter_name_vals_get_n(IPA_CTRL, "bsc", 1, counternames); log("initial bsc rate counters: ", g_ctr_bsc); } private function f_ctrs_bsc_add(charstring countername, integer val := 1) runs on test_CT { f_counter_name_vals_list_add(g_ctr_bsc, 0, countername, val); } /* f_ctrs_bsc_init(); * f_do_thing(); * f_do_thing(); * f_do_other(); * f_ctrs_bsc_add("thing", 2); * f_ctrs_bsc_add("other"); * f_ctrs_bsc_verify(); */ private function f_ctrs_bsc_verify() runs on test_CT { f_counter_name_vals_expect_n(IPA_CTRL, "bsc", g_ctr_bsc); } /* convenience: f_ctrs_bsc_add() and f_ctrs_bsc_verify() in one call. * f_ctrs_bsc_init(); * f_do_thing(); * f_ctrs_bsc_expect("thing", 1); */ private function f_ctrs_bsc_expect(charstring countername, integer val := 1) runs on test_CT { f_ctrs_bsc_add(countername, val); f_ctrs_bsc_verify(); } private function f_shutdown_helper() runs on test_CT { all component.stop; setverdict(pass); mtc.stop; } private function f_legacy_bssap_reset(integer bssap_idx := 0) runs on test_CT { var BSSAP_N_UNITDATA_ind ud_ind; var boolean reset_received := false; timer T := 5.0; BSSAP.send(ts_BSSAP_UNITDATA_req(g_bssap[bssap_idx].sccp_addr_peer, g_bssap[bssap_idx].sccp_addr_own, ts_BSSMAP_Reset(0, g_osmux_enabled))); T.start; alt { [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(g_bssap[bssap_idx].sccp_addr_own, g_bssap[bssap_idx].sccp_addr_peer, tr_BSSMAP_ResetAck(g_osmux_enabled))) { log("Received RESET-ACK in response to RESET, we're ready to go!"); } [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(g_osmux_enabled))) -> value ud_ind { log("Respoding to inbound RESET with RESET-ACK"); BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, ts_BSSMAP_ResetAck(g_osmux_enabled))); reset_received := true; repeat; } [] BSSAP.receive { repeat; } [] T.timeout { log("Timeout waiting for RESET-ACK after sending RESET"); /* If we received a RESET after ours was sent, it may be a race condition where the other peer beacame available after we sent it, but we are in a desired state anyway, so go forward. */ if (not reset_received) { setverdict(fail); } } } } type record IPA_Client { /* IPA Emulation component reference */ IPA_Emulation_CT vc_IPA, /* Unit-ID and other CCM parameters to use for IPA client emulation */ IPA_CCM_Parameters ccm_pars, /* String identifier for this IPA Client */ charstring id, /* Associated RSL Emulation Component (if any). Only used in "Handler mode" */ RSL_Emulation_CT vc_RSL optional } /*! Start the IPA/RSL related bits for one IPA_Client. * \param clnt IPA_Client for which to establish * \param bsc_host IP address / hostname of the BSC * \param bsc_port TCP port number of the BSC * \param i number identifying this BTS * \param handler_mode Start an RSL_Emulation_CT component (true) or not (false) */ function f_ipa_rsl_start(inout IPA_Client clnt, charstring bsc_host, PortNumber bsc_port, integer i, boolean handler_mode := false) runs on test_CT { timer T := 10.0; clnt.id := "IPA" & int2str(i) & "-RSL"; clnt.vc_IPA := IPA_Emulation_CT.create(clnt.id & "-IPA"); clnt.ccm_pars := c_IPA_default_ccm_pars; clnt.ccm_pars.name := "Osmocom TTCN-3 BTS Simulator"; clnt.ccm_pars.unit_id := int2str(1234+i) & "/0/0"; if (handler_mode) { clnt.vc_RSL := RSL_Emulation_CT.create(clnt.id & "-RSL"); connect(clnt.vc_RSL:CCHAN_PT, self:RSL_CCHAN[i]); } map(clnt.vc_IPA:IPA_PORT, system:IPA_CODEC_PT); if (handler_mode) { connect(clnt.vc_IPA:IPA_RSL_PORT, clnt.vc_RSL:IPA_PT); } else { connect(clnt.vc_IPA:IPA_RSL_PORT, self:IPA_RSL[i]); } clnt.vc_IPA.start(IPA_Emulation.main_client(bsc_host, bsc_port, "", 10000+i, clnt.ccm_pars)); if (handler_mode) { clnt.vc_RSL.start(RSL_Emulation.main()); return; } /* wait for IPA RSL link to connect and send ID ACK */ T.start; alt { [] IPA_RSL[i].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) { T.stop; IPA_RSL[i].send(ts_ASP_RSL_UD(ts_RSL_PAGING_LOAD_IND(23))); } [] IPA_RSL[i].receive(ASP_IPA_Event:?) { repeat } [] IPA_RSL[i].receive { repeat } [] T.timeout { setverdict(fail, "Timeout RSL waiting for ASP_IPA_EVENT_ID_ACK"); mtc.stop; } } } function f_ipa_rsl_stop(inout IPA_Client clnt) runs on test_CT { if (not isbound(clnt) or not isbound(clnt.vc_IPA)) { return; } clnt.vc_IPA.stop; if (isbound(clnt.vc_RSL)) { clnt.vc_RSL.stop; } } /* Wait for the OML connection to be brought up by the external osmo-bts-omldummy */ function f_wait_oml(integer bts_nr, charstring status, float secs_max) runs on test_CT { timer T := secs_max; T.start; while (true) { if (f_ctrl_get_bts(IPA_CTRL, bts_nr, "oml-connection-state") == status) { T.stop; /* the 'degraded' state exists from OML connection time, and we have to wait * until all MO's are initialized */ T.start(1.0); T.timeout; return; } f_sleep(0.1); if (not T.running) { setverdict(fail, "Timeout waiting for BTS" & int2str(bts_nr) & " oml-connection-state ", status); mtc.stop; } } } /* global altstep for global guard timer; also takes care of responding RESET witH RESET-ACK */ altstep as_Tguard() runs on test_CT { var BSSAP_N_UNITDATA_ind ud_ind; [] T_guard.timeout { setverdict(fail, "Timeout of T_guard"); mtc.stop; } /* always respond with RESET ACK to RESET */ [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(g_osmux_enabled))) -> value ud_ind { BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, ts_BSSMAP_ResetAck(g_osmux_enabled))); repeat; } } altstep no_bssmap_reset() runs on test_CT { [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(g_osmux_enabled))) { setverdict(fail, "unexpected BSSMAP Reset"); mtc.stop; } } function f_init_mgcp(charstring id) runs on test_CT { id := id & "-MGCP"; var MGCPOps ops := { create_cb := refers(MGCP_Emulation.ExpectedCreateCallback), unitdata_cb := refers(MGCP_Emulation.DummyUnitdataCallback) }; var MGCP_conn_parameters mgcp_pars := { callagent_ip := mp_bsc_ip, callagent_udp_port := -1, mgw_ip := mp_test_ip, mgw_udp_port := 2427, /* Enable it for SCCPlite, since we have 2 MGCP sockets towards MGW (UDP one + the on with MGCP over IPA forwarded from MSC one) */ multi_conn_mode := (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_SCCPlite_SERVER) }; vc_MGCP := MGCP_Emulation_CT.create(id); vc_MGCP.start(MGCP_Emulation.main(ops, mgcp_pars, id)); } /* Enable or disable (current default) Osmux. When enabling, BSSMAP Reset * contains extra IE (OsmuxSupport) and osmo-bsc will handle AssignReq with * OsmuxCID IE. */ private function f_vty_allow_osmux(boolean allow) runs on test_CT { f_vty_enter_cfg_msc(BSCVTY, 0); if (allow) { f_vty_transceive(BSCVTY, "osmux on"); } else { f_vty_transceive(BSCVTY, "osmux off"); } f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); g_osmux_enabled := allow; } function f_init_vty(charstring id := "foo") runs on test_CT { if (BSCVTY.checkstate("Mapped")) { /* skip initialization if already executed once */ return; } map(self:BSCVTY, system:BSCVTY); f_vty_set_prompts(BSCVTY); f_vty_transceive(BSCVTY, "enable"); f_cs7_inst_0_cfg(BSCVTY, {"sccp-timer ias " & int2str(g_bsc_sccp_timer_ias), "sccp-timer iar " & int2str(g_bsc_sccp_timer_iar)}); } private function f_logp(TELNETasp_PT pt, charstring log_msg) { // log on TTCN3 log output log(log_msg); // log in stderr log f_vty_transceive(pt, "logp lglobal notice TTCN3 f_logp(): " & log_msg); } private function f_sysinfo_seen(integer rsl_idx, RSL_Message rsl) runs on test_CT { if (rsl_idx >= lengthof(g_system_information)) { g_system_information[rsl_idx] := SystemInformationConfig_omit } f_sysinfo_dec_raw(g_system_information[rsl_idx], rsl); } altstep as_catch_RSL_sysinfo(integer rsl_idx) runs on test_CT { var ASP_RSL_Unitdata rx_rsl_ud; /* For handler_mode := false, receiving the RSL bootstrap messages directly on IPA_RSL */ [] IPA_RSL[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_NO_BCCH_INFO)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] IPA_RSL[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_BCCH_INFO)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] IPA_RSL[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_NO_SACCH_FILL)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] IPA_RSL[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_SACCH_FILL)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } /* For handler_mode := true, receiving the RSL bootstrap messages via RSL_Emulation */ [] RSL_CCHAN[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_NO_BCCH_INFO)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] RSL_CCHAN[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_BCCH_INFO)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] RSL_CCHAN[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_NO_SACCH_FILL)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } [] RSL_CCHAN[rsl_idx].receive(tr_ASP_RSL_UD(tr_RSL_SACCH_FILL)) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); repeat; } } /* TODO: use BooleanList from COMMON/src/General_Types.ttcn */ private type record of boolean my_BooleanList; private function f_vty_msc_allow_attach(TELNETasp_PT pt, my_BooleanList allow_attach_list) { var charstring config := f_vty_transceive_ret(pt, "show running-config"); for (var integer msc_nr := 0; msc_nr < sizeof(allow_attach_list); msc_nr := msc_nr+1) { if (f_strstr(config, "\nmsc " & int2str(msc_nr) & "\n") < 0) { /* There is no 'msc N' for this msc_nr in the running config, so don't create an empty msc by * stepping into that config node. */ log("msc ", msc_nr, " is not configured, skipping"); continue; } f_vty_enter_cfg_msc(pt, msc_nr); if (allow_attach_list[msc_nr]) { /* strict := false: ignore if osmo-bsc does not support this config option (latest build) */ f_vty_transceive(pt, "allow-attach", strict := false); } else { f_vty_transceive(pt, "no allow-attach", strict := false); } f_vty_transceive(pt, "exit"); f_vty_transceive(pt, "exit"); } } /* global initialization function * \param nr_bts Number of BTSs we should start/bring up * \param handler_mode Start an RSL_Emulation_CT component (true) or not (false). * \param nr_msc Number of virtual MSCs to bring up to connect to osmo-bsc. */ function f_init(integer nr_bts := NUM_BTS, boolean handler_mode := false, boolean allow_osmux := false, integer nr_msc := 1, float guard_timeout := 30.0) runs on test_CT { var integer bssap_idx; if (g_initialized) { return; } g_initialized := true; T_guard.start(guard_timeout); activate(as_Tguard()); f_init_vty("VirtMSC"); if (mp_enable_osmux_test) { f_vty_allow_osmux(allow_osmux); } var my_BooleanList allow_attach := { false, false, false }; f_init_statsd("VirtMSC", vc_STATSD, mp_test_ip, mp_bsc_statsd_port); for (bssap_idx := 0; bssap_idx < nr_msc; bssap_idx := bssap_idx+1) { allow_attach[bssap_idx] := true; /* Call a function of our 'parent component' RAN_Adapter_CT to start the * MSC-side BSSAP emulation */ if (handler_mode) { var RanOps ranops := MSC_RanOps; ranops.use_osmux := g_osmux_enabled; f_ran_adapter_init(g_bssap[bssap_idx], mp_bssap_cfg[bssap_idx], "VirtMSC", ranops); connect(self:SCCPLITE_IPA_CTRL, g_bssap[bssap_idx].vc_RAN:CTRL_CLIENT); f_ran_adapter_start(g_bssap[bssap_idx]); } else { f_ran_adapter_init(g_bssap[bssap_idx], mp_bssap_cfg[bssap_idx], "VirtMSC", omit); connect(self:BSSAP, g_bssap[bssap_idx].vc_SCCP:SCCP_SP_PORT); f_ran_adapter_start(g_bssap[bssap_idx]); f_legacy_bssap_reset(); } } if (mp_enable_lcs_tests) { if (handler_mode) { f_bssap_le_adapter_init(g_bssap_le, mp_bssap_le_cfg, "VirtSMLC", SMLC_BssapLeOps); } else { f_bssap_le_adapter_init(g_bssap_le, mp_bssap_le_cfg, "VirtSMLC", omit); connect(self:BSSAP_LE, g_bssap_le.vc_SCCP:SCCP_SP_PORT); } f_bssap_le_adapter_start(g_bssap_le); } /* start the test with exactly all enabled MSCs allowed to attach */ f_vty_msc_allow_attach(BSCVTY, allow_attach); f_ipa_ctrl_start(mp_bsc_ip, mp_bsc_ctrl_port); f_init_mgcp("VirtMSC"); for (var integer i := 0; i < nr_bts; i := i+1) { f_init_bts(i, handler_mode); } } function f_init_bts(integer bts_idx := 0, boolean handler_mode := false) runs on test_CT { /* wait until osmo-bts-omldummy has respawned */ f_wait_oml(bts_idx, "degraded", 5.0); /* start RSL connection */ f_ipa_rsl_start(bts[bts_idx].rsl, mp_bsc_ip, mp_bsc_rsl_port, bts_idx, handler_mode); /* wait until BSC tells us "connected" */ f_wait_oml(bts_idx, "connected", 5.0); } function f_init_bts_and_check_sysinfo(integer bts_idx := 0, boolean handler_mode := false, template SystemInformationConfig expect_si) runs on test_CT { var default sysinfo := activate(as_catch_RSL_sysinfo(bts_idx)); f_init_bts(bts_idx, handler_mode); /* Give some time to (hopefully/most likely) collect all system informations from RSL startup. * We could stop as soon as all expected SI are received, but then we might miss SI that we don't expect and * that might be sent afterwards. So rather give a generous timeout and be quite sure to catch all SI. */ f_sleep(5.0); log("RSL ", bts_idx, " SYSTEM INFORMATION: ", g_system_information[bts_idx]); deactivate(sysinfo); if (match(g_system_information[bts_idx], expect_si)) { setverdict(pass); } else { log("RSL ", bts_idx, ": EXPECTED SI: ", expect_si); log("RSL ", bts_idx, ": GOT SI: ", g_system_information[bts_idx]); setverdict(fail, "received SI does not match expectations"); return; } } /* expect to receive a RSL message matching a specified template on a given BTS / stream */ function f_exp_ipa_rx(integer bts_nr, template (present) RSL_Message t_rx, float t_secs := 2.0, IpaStreamId sid := IPAC_PROTO_RSL_TRX0) runs on test_CT return RSL_Message { var ASP_RSL_Unitdata rx_rsl_ud; timer T := t_secs; T.start; alt { [] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(t_rx, sid)) -> value rx_rsl_ud { T.stop; } [] IPA_RSL[bts_nr].receive { repeat; } [] T.timeout { setverdict(fail, "Timeout expecting ", t_rx); mtc.stop; } } return rx_rsl_ud.rsl; } /* helper function to transmit RSL on a given BTS/stream */ function f_ipa_tx(integer bts_nr, template (value) RSL_Message t_tx, IpaStreamId sid := IPAC_PROTO_RSL_TRX0) runs on test_CT { IPA_RSL[bts_nr].send(ts_ASP_RSL_UD(t_tx, sid)); } /* verify we get a CHAN_ACT after CHAN RQD */ testcase TC_chan_act_noreply() runs on test_CT { var BSSAP_N_UNITDATA_ind ud_ind; var RSL_Message rsl_unused; f_init(1); IPA_RSL[0].send(ts_ASP_RSL_UD(ts_RSL_CHAN_RQD('23'O, 23))); rsl_unused := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); f_shutdown_helper(); } /* verify if the "chreq:total" counter increments as expected */ testcase TC_chan_act_counter() runs on test_CT { var BSSAP_N_UNITDATA_ind ud_ind; var integer chreq_total; var RSL_Message rsl_unused; f_init(1); chreq_total := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total"); IPA_RSL[0].send(ts_ASP_RSL_UD(ts_RSL_CHAN_RQD('23'O, 23))); rsl_unused := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total", chreq_total+1); f_shutdown_helper(); } /* CHAN RQD -> CHAN ACT -> CHAN ACT ACK -> RF CHAN REL */ private function f_TC_chan_act_ack_noest(OCT1 ra := '23'O) runs on test_CT { var RSL_Message rx_rsl; /* Send CHAN RQD and wait for allocation; acknowledge it */ var RslChannelNr chan_nr := f_chreq_act_ack(ra); /* expect BSC to disable the channel again if there's no RLL EST IND */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), T3101_MAX); f_shutdown_helper(); } /* Normal variant */ testcase TC_chan_act_ack_noest() runs on test_CT { f_init(1); f_TC_chan_act_ack_noest(); } /* Emergency call variant */ testcase TC_chan_act_ack_noest_emerg() runs on test_CT { /* See also: 3GPP TS 04.08, Table 9.9, ra=101xxxxx */ f_init(1); f_vty_allow_emerg_bts(true, 0); f_TC_chan_act_ack_noest(ra := 'A5'O); } /* Emergency call variant, but emergency calls are not allowed */ testcase TC_chan_rqd_emerg_deny() runs on test_CT { /* See also: 3GPP TS 04.08, Table 9.9, ra=101xxxxx */ var RSL_Message rx_rsl; var GsmRrMessage rr; f_init(1); f_vty_allow_emerg_bts(false, 0); IPA_RSL[0].clear; f_ipa_tx(0, ts_RSL_CHAN_RQD('A5'O, 23)); rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeC(RSL_MT_IMMEDIATE_ASSIGN_CMD)); rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload); if (rr.header.message_type == IMMEDIATE_ASSIGNMENT_REJECT) { setverdict(pass); } else { setverdict(fail, "immediate assignment not rejected"); } } /* Test behavior if MSC never answers to CR */ testcase TC_chan_act_ack_est_ind_noreply() runs on test_CT { var RslLinkId main_dcch := valueof(ts_RslLinkID_DCCH(0)); var IpaStreamId sid := IPAC_PROTO_RSL_TRX0; var RSL_Message rx_rsl; var ASP_RSL_Unitdata rx_rsl_ud; f_init(1); /* Send CHAN RQD and wait for allocation; acknowledge it */ var RslChannelNr chan_nr := f_chreq_act_ack(); var octetstring l3 := '00010203040506'O f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3)); BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))); /* expect BSC to disable the channel again if there's no response from MSC */ /* MS waits 20s (T3210) at LU; 10s (T3230) at CM SERV REQ and 5s (T3220) AT detach */ f_expect_chan_rel(0, chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Test behavior if MSC answers with CREF to CR */ testcase TC_chan_act_ack_est_ind_refused() runs on test_CT { var BSSAP_N_CONNECT_ind rx_c_ind; var RSL_Message rx_rsl; f_init(1); /* Send CHAN RQD and wait for allocation; acknowledge it */ var RslChannelNr chan_nr := f_chreq_act_ack(); var octetstring l3 := '00010203040506'O f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3)); BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) -> value rx_c_ind; BSSAP.send(ts_BSSAP_DISC_req(rx_c_ind.connectionId, 0)); /* expect BSC to disable the channel */ f_expect_chan_rel(0, chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* CHAN RQD -> CHAN ACT -> CHAN ACT NACK -> RF CHAN REL */ testcase TC_chan_act_nack() runs on test_CT { var RSL_Message rx_rsl; var integer chact_nack; f_init(1); chact_nack := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chan_act:nack"); f_ipa_tx(0, ts_RSL_CHAN_RQD('33'O, 33)); rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); var RslChannelNr chan_nr := rx_rsl.ies[0].body.chan_nr; f_ipa_tx(0, ts_RSL_CHAN_ACT_NACK(chan_nr, RSL_ERR_EQUIPMENT_FAIL)); /* wait for some time to hope the NACK arrives before the CTRL GET below */ f_sleep(0.5); f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chan_act:nack", chact_nack+1); f_shutdown_helper(); } /* Test for channel exhaustion due to RACH overload */ testcase TC_chan_exhaustion() runs on test_CT { var ASP_RSL_Unitdata rsl_ud; var integer i; var integer chreq_total, chreq_nochan; f_init(1); chreq_total := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total"); chreq_nochan := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:no_channel"); /* GSM 04.08 Table 9.9a: * RA = '33'O -> Establishment cause = 0011xxxx (MS dual rate capable and asks for "TCH/H or TCH/F"). * With current setup, expect 4xSDCCH + 4xTCH/F + 1xTCH/H to succeed */ for (i := 0; i < NUM_TCHF_PER_BTS + NUM_TCHH_PER_BTS + NUM_SDCCH_PER_BTS; i := i+1) { var RslChannelNr chan_nr := f_chreq_act_ack('33'O, i); } IPA_RSL[0].clear; f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total", chreq_total + NUM_TCHF_PER_BTS + NUM_TCHH_PER_BTS + NUM_SDCCH_PER_BTS); /* now expect additional channel activations to fail */ f_ipa_tx(0, ts_RSL_CHAN_RQD('42'O, 42)); alt { [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) { setverdict(fail, "Received CHAN ACT ACK without resources?!?"); } [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_IMM_ASSIGN(?))) -> value rsl_ud { var GsmRrMessage rr; /* match on IMM ASS REJ */ rr := dec_GsmRrMessage(rsl_ud.rsl.ies[1].body.full_imm_ass_info.payload); if (rr.header.message_type == IMMEDIATE_ASSIGNMENT_REJECT) { f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total", chreq_total + NUM_TCHF_PER_BTS + NUM_TCHH_PER_BTS + NUM_SDCCH_PER_BTS+1); f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:no_channel", chreq_nochan+1); setverdict(pass); } else { repeat; } } [] IPA_RSL[0].receive { repeat; } } f_shutdown_helper(); } /* Test channel deactivation due to silence from MS */ testcase TC_chan_deact_silence() runs on test_CT { var RslChannelNr chan_nr; f_init(1); /* Request for a dedicated channel */ chan_nr := f_chreq_act_ack('23'O); /* Wait some time until the channel is released */ f_sleep(2.0); /* Expect CHANnel RELease */ alt { [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL))) { log("Received CHANnel RELease"); setverdict(pass); } [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_IMM_ASSIGN(?))) { /* See OS#3709, OsmoBSC should not send Immediate * Assignment Reject since a dedicated channel was * already allocated, and Immediate Assignment was * already sent. */ setverdict(fail, "Unexpected Immediate Assignment!"); } [] IPA_RSL[0].receive { setverdict(fail, "Unexpected RSL message!"); } } f_shutdown_helper(); } /*********************************************************************** * Assignment Testing ***********************************************************************/ /* Verify that the BSC refuses any BSSAP connection from the MSC (They are all BSC->MSC direction, * except for the inter-BSC handover, MT side) */ testcase TC_outbound_connect(integer bssap_idx := 0) runs on test_CT { f_init(1); BSSAP.send(ts_BSSAP_CONNECT_req(g_bssap[bssap_idx].sccp_addr_peer, g_bssap[bssap_idx].sccp_addr_own, 2342, ts_BSSMAP_AssignmentReq)); BSSAP.receive(tr_BSSAP_DISC_ind(2342, ?, ?)); f_shutdown_helper(); } /* Test behavior if MSC answers with CREF to CR */ testcase TC_assignment_cic_only(integer bssap_idx := 0) runs on test_CT { var BSSAP_N_CONNECT_ind rx_c_ind; var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00000000'O); if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) { /* send assignment without AoIP IEs */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_AssignmentReq(ts_BSSMAP_IE_CIC(0, 1)))); } else { /* Send assignmetn without CIC in IPA case */ var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(ts_BSSMAP_IE_AoIP_TLA4('01020304'O, 2342)); BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_AssignmentReq(omit, tla))); } alt { [] BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_AssignmentComplete)) { setverdict(fail, "AoIP BSC cannot accept ASSIGNMENT without AoIP Transport IE"); } /* TODO: Actually expect GSM0808_CAUSE_REQ_A_IF_TYPE_NOT_SUPP */ [] BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_AssignmentFail)) { setverdict(pass); } [] BSSAP.receive { repeat; } } f_shutdown_helper(); } /* generate an assignment request for either AoIP or SCCPlite */ function f_gen_ass_req(boolean osmux_enabled := false, integer bssap_idx := 0, charstring aoip_tla := "1.2.3.4") return PDU_BSSAP { var PDU_BSSAP ass_cmd; var BSSMAP_IE_Osmo_OsmuxCID osmux_cid := valueof(ts_OsmuxCID(0)); if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) { var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(f_ts_BSSMAP_IE_AoIP_TLA(aoip_tla, 2342)); if (osmux_enabled) { ass_cmd := valueof(ts_BSSMAP_AssignmentReq(omit, tla, osmux_cid)); } else { ass_cmd := valueof(ts_BSSMAP_AssignmentReq(omit, tla)); } } else { var BSSMAP_IE_CircuitIdentityCode cic := valueof(ts_BSSMAP_IE_CIC(0,1)); ass_cmd := valueof(ts_BSSMAP_AssignmentReq(cic, omit)); } return ass_cmd; } function f_gen_handover_req(integer bssap_idx := 0, charstring aoip_tla := "1.2.3.4") return PDU_BSSAP { var PDU_BSSAP ho_req; if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) { var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(f_ts_BSSMAP_IE_AoIP_TLA(aoip_tla, 2342)); ho_req := valueof(ts_BSSMAP_HandoverRequest(omit, tla)); } else { var BSSMAP_IE_CircuitIdentityCode cic := valueof(ts_BSSMAP_IE_CIC(0,1)); ho_req := valueof(ts_BSSMAP_HandoverRequest(cic, omit)); } return ho_req; } /* generate an assignment complete template for either AoIP or SCCPlite */ function f_gen_exp_compl(boolean expect_osmux := false, integer bssap_idx := 0) return template PDU_BSSAP { var template PDU_BSSAP exp_compl; var BSSMAP_IE_Osmo_OsmuxCID osmux_cid := valueof(ts_OsmuxCID(0)); if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) { if (expect_osmux) { exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, osmux_cid); } else { exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, omit); } } else { /* CIC is optional "*" as the MSC allocated it */ exp_compl := tr_BSSMAP_AssignmentComplete(*, omit); } return exp_compl; } /* Run everything required up to sending a caller-specified assignment command and expect response */ function f_assignment_exp(PDU_BSSAP ass_cmd, template PDU_BSSAP exp, charstring fail_text) runs on test_CT { var BSSAP_N_CONNECT_ind rx_c_ind; var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00000000'O); /* send assignment without AoIP IEs */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ass_cmd)); alt { [] BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_AssignmentComplete)) { if (ischosen(exp.pdu.bssmap.assignmentComplete)) { setverdict(pass); } else { setverdict(fail, fail_text); } } [] BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_AssignmentFail)) { if (ischosen(exp.pdu.bssmap.assignmentFailure)) { setverdict(pass); } else { setverdict(fail, fail_text); } } [] BSSAP.receive { repeat; } } } testcase TC_assignment_csd() runs on test_CT { var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeCSD); //exp_fail.pdu.bssmap.assignmentFailure.cause.causeValue := int2bit(enum2int(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL), 7); f_assignment_exp(ass_cmd, exp_fail, "BSC accepted Assignment for CSD"); f_shutdown_helper(); } testcase TC_assignment_ctm() runs on test_CT { var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeCTM); //exp_fail.pdu.bssmap.assignmentFailure.cause.causeValue := int2bit(enum2int(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL), 7); f_assignment_exp(ass_cmd, exp_fail, "BSC accepted Assignment for Speech+CTM"); f_shutdown_helper(); } type record DchanTuple { integer sccp_conn_id, RslChannelNr rsl_chan_nr } /* Send CHAN RQD and wait for allocation; acknowledge it */ private function f_chreq_act_ack(OCT1 ra := '23'O, GsmFrameNumber fn := 23) runs on test_CT return RslChannelNr { var RSL_Message rx_rsl; f_ipa_tx(0, ts_RSL_CHAN_RQD(ra, fn)); rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); var RslChannelNr chan_nr := rx_rsl.ies[0].body.chan_nr; f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(chan_nr, fn+10)); rx_rsl := f_exp_ipa_rx(0, tr_RSL_IMM_ASSIGN(0)); return chan_nr; } /* helper function to establish a dedicated channel via BTS and MSC */ function f_est_dchan(OCT1 ra, GsmFrameNumber fn, octetstring l3) runs on test_CT return DchanTuple { var BSSAP_N_CONNECT_ind rx_c_ind; var DchanTuple dt; /* Send CHAN RQD and wait for allocation; acknowledge it */ dt.rsl_chan_nr := f_chreq_act_ack(ra, fn); f_ipa_tx(0, ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3)); BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) -> value rx_c_ind; dt.sccp_conn_id := rx_c_ind.connectionId; BSSAP.send(ts_BSSAP_CONNECT_res(dt.sccp_conn_id)); return dt; } /* expect RF CAN REL from BTS, acknowledge it and clear the MSC side */ private function f_exp_chan_rel_and_clear(DchanTuple dt, integer bts_nr := 0) runs on test_CT { var RSL_Message rx_rsl; /* expect BSC to disable the channel */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), T3101_MAX); /* respond with CHAN REL ACK */ f_ipa_tx(0, ts_RSL_RF_CHAN_REL_ACK(dt.rsl_chan_nr)); /* expect Clear Complete from BSC */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)); /* MSC disconnects as instructed. */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } /* Test behavior of channel release after unilateral RLL REL IND (DISC from MS) */ testcase TC_chan_rel_rll_rel_ind() runs on test_CT { var BSSAP_N_DATA_ind rx_di; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* simulate RLL REL IND */ f_ipa_tx(0, ts_RSL_REL_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)))); /* expect Clear Request on MSC side */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearRequest)) -> value rx_di; /* Instruct BSC to clear channel */ var BssmapCause cause := bit2int(rx_di.userData.pdu.bssmap.clearRequest.cause.causeValue); BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(cause))); /* expect BSC to disable the channel */ f_exp_chan_rel_and_clear(dt, 0); /* wait for SCCP emulation to do its job */ f_sleep(1.0); f_shutdown_helper(); } /* Test behavior of channel release after CONN FAIL IND from BTS */ testcase TC_chan_rel_conn_fail() runs on test_CT { var BSSAP_N_DATA_ind rx_di; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* simulate CONN FAIL IND */ f_ipa_tx(0, ts_RSL_CONN_FAIL_IND(dt.rsl_chan_nr, RSL_ERR_RADIO_LINK_FAIL)); /* TODO: different cause values? */ /* expect Clear Request from BSC */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearRequest)) -> value rx_di; /* Instruct BSC to clear channel */ var BssmapCause cause := bit2int(rx_di.userData.pdu.bssmap.clearRequest.cause.causeValue); BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(cause))); /* expect BSC to disable the channel */ f_exp_chan_rel_and_clear(dt, 0); /* wait for SCCP emulation to do its job */ f_sleep(1.0); f_shutdown_helper(); } /* Test behavior of early CONN FAIL IND from BTS (before EST IND!) */ /* See also https://www.osmocom.org/issues/3182 */ testcase TC_early_conn_fail() runs on test_CT { var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); /* BTS->BSC: Send CHAN RQD and wait for allocation; acknowledge it */ dt.rsl_chan_nr := f_chreq_act_ack(f_rnd_ra_cs(), 23); /* BTS->BSC: simulate CONN FAIL IND */ f_ipa_tx(0, ts_RSL_CONN_FAIL_IND(dt.rsl_chan_nr, RSL_ERR_RADIO_LINK_FAIL)); /* BTS->BSC: Expect RF channel release from BSC on Abis */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), 10.0); /* BTS<-BSC: respond with CHAN REL ACK */ f_ipa_tx(0, ts_RSL_RF_CHAN_REL_ACK(dt.rsl_chan_nr)); f_shutdown_helper(); } /* Test behavior of late CONN FAIL IND from BTS (ater REL IND!) */ /* See also https://www.osmocom.org/issues/3182 */ testcase TC_late_conn_fail() runs on test_CT { var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* BSC<-MSC: Instruct BSC to clear connection */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(0))); /* BTS->BSC: expect BSC to deactivate SACCH */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_DEACT_SACCH(dt.rsl_chan_nr)); /* BTS->BSC: simulate a late CONN FAIL IND from BTS */ f_ipa_tx(0, ts_RSL_CONN_FAIL_IND(dt.rsl_chan_nr, RSL_ERR_RADIO_LINK_FAIL)); /* BTS<-BSC: Expect RF channel release from BSC on Abis */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), 10.0); /* BTS->BSC: respond with CHAN REL ACK */ f_ipa_tx(0, ts_RSL_RF_CHAN_REL_ACK(dt.rsl_chan_nr)); /* BSC->MSC: expect Clear Complete from BSC */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)); /* BSC<-MSC: MSC disconnects as requested. */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_shutdown_helper(); } function f_expect_chan_rel(integer bts_nr, RslChannelNr rsl_chan_nr, boolean expect_deact_sacch := true, boolean expect_rr_chan_rel := true, boolean expect_rll_rel_req := true, boolean handle_rll_rel := true, boolean is_csfb := false, template CellSelIndValue csfb_expect_cells := omit, template RR_Cause expect_rr_cause := ? ) runs on test_CT { var RslLinkId main_dcch := valueof(ts_RslLinkID_DCCH(0)); var boolean got_deact_sacch := false; var boolean got_rr_chan_rel := false; var boolean got_rll_rel_req := false; var ASP_RSL_Unitdata ud; var RSL_IE_Body l3_ie; var PDU_ML3_NW_MS l3; var RR_Cause got_cause; log("f_expect_chan_rel() expecting: expect_deact_sacch=", expect_deact_sacch, " expect_rr_chan_rel=", expect_rr_chan_rel, " expect_rll_rel_req=", expect_rll_rel_req); alt { [] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_DEACT_SACCH(rsl_chan_nr))) { got_deact_sacch := true; repeat; } [is_csfb] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_DATA_REQ(rsl_chan_nr, ?, decmatch tr_RRM_RR_RELEASE_CSFB))) -> value ud { got_rr_chan_rel := true; if (f_rsl_find_ie(ud.rsl, RSL_IE_L3_INFO, l3_ie) == false) { setverdict(fail, "cannot find L3"); mtc.stop; } l3 := dec_PDU_ML3_NW_MS(l3_ie.l3_info.payload); if (not istemplatekind(csfb_expect_cells, "omit")) { var CellSelIndValue cells := dec_CellSelIndValue( l3.msgs.rrm.channelRelease.cellSelectionIndicator.cellSelectionIndicatorValue); log("GOT RR CHANNEL RELEASE CSFB CELLS: ", cells); if (match(cells, csfb_expect_cells)) { setverdict(pass); } else { log("EXPECTED CSFB CELLS: ", csfb_expect_cells); setverdict(fail, "Received CSFB cells list on RR Channel Release does not match expectations"); } } if (not istemplatekind(expect_rr_cause, "omit")) { int2enum(oct2int(l3.msgs.rrm.channelRelease.rRCause.valuePart), got_cause); log("GOT CAUSE CODE: ", l3.msgs.rrm.channelRelease.rRCause.valuePart, " = ", got_cause); if (match(got_cause, expect_rr_cause)) { setverdict(pass); } else { log("EXPECTED CAUSE CODE: ", expect_rr_cause); setverdict(fail, "Received RR Channel Release Cause code does not match expectations"); } } repeat; } [not is_csfb] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_DATA_REQ(rsl_chan_nr, ?, decmatch tr_RRM_RR_RELEASE))) -> value ud { got_rr_chan_rel := true; if (not istemplatekind(expect_rr_cause, "omit")) { if (f_rsl_find_ie(ud.rsl, RSL_IE_L3_INFO, l3_ie) == false) { setverdict(fail, "cannot find L3"); mtc.stop; } l3 := dec_PDU_ML3_NW_MS(l3_ie.l3_info.payload); int2enum(oct2int(l3.msgs.rrm.channelRelease.rRCause.valuePart), got_cause); log("GOT CAUSE CODE: ", l3.msgs.rrm.channelRelease.rRCause.valuePart, " = ", got_cause); if (match(got_cause, expect_rr_cause)) { setverdict(pass); } else { log("EXPECTED CAUSE CODE: ", expect_rr_cause); setverdict(fail, "Received RR Channel Release Cause code does not match expectations"); } } repeat; } [] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_REL_REQ(rsl_chan_nr, ?))) { got_rll_rel_req := true; /* FIXME: Why are we getting this for LinkID SACCH? */ if (handle_rll_rel) { f_ipa_tx(0, ts_RSL_REL_CONF(rsl_chan_nr, main_dcch)); } repeat; } [] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL))) { /* respond with CHAN REL ACK */ f_ipa_tx(0, ts_RSL_RF_CHAN_REL_ACK(rsl_chan_nr)); } /* ignore any user data */ [] IPA_RSL[bts_nr].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeR(?))) { repeat; } } log("f_expect_chan_rel() summary: got_deact_sacch=", got_deact_sacch, " got_rr_chan_rel=", got_rr_chan_rel, " got_rll_rel_req=", got_rll_rel_req); if (expect_deact_sacch != got_deact_sacch) { setverdict(fail, "f_expect_chan_rel(): expect_deact_sacch=", expect_deact_sacch, " got_deact_sacch=", got_deact_sacch); } if (expect_rr_chan_rel != got_rr_chan_rel) { setverdict(fail, "f_expect_chan_rel(): expect_rr_chan_rel=", expect_rr_chan_rel, " got_rr_chan_rel=", got_rr_chan_rel); } if (expect_rll_rel_req != got_rll_rel_req) { setverdict(fail, "f_expect_chan_rel(): expect_rll_rel_req=", expect_rll_rel_req, " got_rll_rel_req=", got_rll_rel_req); } } /* Test behavior of channel release after hard Clear Command from MSC */ testcase TC_chan_rel_hard_clear() runs on test_CT { var BSSAP_N_DATA_ind rx_di; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* Instruct BSC to clear channel */ var BssmapCause cause := 0; BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(cause))); /* expect Clear Complete from BSC on A */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)) { /* release the SCCP connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Test behavior of channel release after Clear Command with CSFB indicator from MSC */ testcase TC_chan_rel_hard_clear_csfb() runs on test_CT { var BSSAP_N_DATA_ind rx_di; var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* Instruct BSC to clear channel */ var BssmapCause cause := 0; BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommandCSFB(cause))); /* expect Clear Complete from BSC on A */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)) { /* release the SCCP connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false, is_csfb := true); f_shutdown_helper(); } /* Test behavior of channel release after hard RLSD from MSC */ testcase TC_chan_rel_hard_rlsd() runs on test_CT { var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* release the SCCP connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Test behavior of channel release after hard RLSD from MSC and MS is not responding to RLL REL REQ */ testcase TC_chan_rel_hard_rlsd_ms_dead() runs on test_CT { var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* release the SCCP connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Test behavior of channel release after BSSMAP RESET from MSC */ testcase TC_chan_rel_a_reset() runs on test_CT { var DchanTuple dt; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); /* Clear the queue, it might still contain stuff like IMMEDIATE ASSIGN */ IPA_RSL[0].clear; /* perform BSSAP RESET, expect RESET ACK and DISC.ind on connection */ BSSAP.send(ts_BSSAP_UNITDATA_req(g_bssap[0].sccp_addr_peer, g_bssap[0].sccp_addr_own, ts_BSSMAP_Reset(0, g_osmux_enabled))); interleave { [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(g_bssap[0].sccp_addr_own, g_bssap[0].sccp_addr_peer, tr_BSSMAP_ResetAck(g_osmux_enabled))) { } [] BSSAP.receive(tr_BSSAP_DISC_ind(dt.sccp_conn_id, ?, ?)) { } } f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Verify T(iar) triggers and releases the channel */ testcase TC_chan_rel_sccp_tiar_timeout() runs on test_CT { var DchanTuple dt; /* Set T(iar) in BSC low enough that it will trigger before other side has time to keep alive with a T(ias). Keep recommended ratio of T(iar) >= T(ias)*2 */ g_bsc_sccp_timer_ias := 2; g_bsc_sccp_timer_iar := 5; f_init(1); dt := f_est_dchan('23'O, 23, '00010203040506'O); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } private function f_tc_chan_rel_rr_cause(myBSSMAP_Cause clear_cmd_cause, template RR_Cause expect_rr_cause) runs on test_CT { var DchanTuple dt; dt := f_est_dchan('23'O, 23, '00010203040506'O); var BssmapCause cause := 0; BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(enum2int(clear_cmd_cause)))); BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)) { BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false, expect_rr_cause := expect_rr_cause); f_shutdown_helper(); } /* Test that Clear Command cause codes affect the RR Channel Release cause code */ testcase TC_chan_rel_rr_cause() runs on test_CT { f_init(1); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_CALL_CONTROL, GSM48_RR_CAUSE_NORMAL); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_HANDOVER_SUCCESSFUL, GSM48_RR_CAUSE_NORMAL); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_PREEMPTION, GSM48_RR_CAUSE_PREMPTIVE_REL); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, GSM48_RR_CAUSE_ABNORMAL_UNSPEC); f_tc_chan_rel_rr_cause(GSM0808_CAUSE_EQUIPMENT_FAILURE, GSM48_RR_CAUSE_ABNORMAL_UNSPEC); } /* Test behavior if RSL EST IND for non-active channel */ testcase TC_rll_est_ind_inact_lchan() runs on test_CT { timer T := 2.0; f_init(1); var octetstring l3 := '00010203040506'O; var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(6)); f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3)); T.start; alt { [] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) { setverdict(fail, "MSC received COMPL L3 for non-active lchan"); } [] BSSAP.receive {} [] IPA_RSL[0].receive {} [] T.timeout {} } f_shutdown_helper(); } /* Test behavior if RSL EST IND for invalid SAPI */ testcase TC_rll_est_ind_inval_sapi1() runs on test_CT { var RslChannelNr chan_nr; f_init(1); chan_nr := f_chreq_act_ack() var octetstring l3 := '00010203040506'O; f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(1)), l3)); timer T := 2.0; T.start; alt { [] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) { setverdict(fail, "MSC received COMPL L3 for invalid SAPI 1"); } [] BSSAP.receive { repeat; } [] IPA_RSL[0].receive { repeat; } [] T.timeout {} } f_shutdown_helper(); } /* Test behavior if RSL EST IND for invalid SAPI */ testcase TC_rll_est_ind_inval_sapi3() runs on test_CT { timer T := 2.0; f_init(1); var RslChannelNr chan_nr := f_chreq_act_ack(); var octetstring l3 := '00010203040506'O; f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(3)), l3)); T.start; alt { [] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) { setverdict(fail, "MSC received COMPL L3 for invalid SAPI 3"); } [] BSSAP.receive { repeat; } [] IPA_RSL[0].receive { repeat; } [] T.timeout {} } f_shutdown_helper(); } /* Test behavior if RSL EST IND for invalid SACCH */ testcase TC_rll_est_ind_inval_sacch() runs on test_CT { timer T := 2.0; f_init(1); var RslChannelNr chan_nr := f_chreq_act_ack(); var octetstring l3 := '00010203040506'O; f_ipa_tx(0, ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_SACCH(0)), l3)); T.start; alt { [] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3))) { setverdict(fail, "MSC received COMPL L3 for invalid Link SACCH"); } [] BSSAP.receive { repeat; } [] IPA_RSL[0].receive { repeat; } [] T.timeout {} } f_shutdown_helper(); } /* Verify DLCI / RSL Link ID conversion for MO/MT messages on SAPI0/SAPI3 */ private function f_TC_tch_dlci_link_id_sapi(charstring id) runs on MSC_ConnHdlr { var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); /* SAPI0 has already been established by f_establish_fully(), establish SAPI3 */ RSL.send(ts_RSL_EST_IND(g_chan_nr, ts_RslLinkID_SACCH(3), '0904'O)); /* Expect BSSAP/DTAP on SAPI3 (DLCI IE) */ BSSAP.receive(PDU_BSSAP:{ discriminator := '1'B, spare := '0000000'B, dlci := 'C3'O, lengthIndicator := ?, pdu := { dtap := '0904'O } }); /* Send messages on DCCH/SAPI0 and ACCH/SAPI3 */ for (var integer i := 0; i < 32; i := i + 1) { var octetstring l3 := '09'O & f_rnd_octstring(14); var template (value) RslLinkId link_id; var template (value) OCT1 dlci; if (i mod 2 == 0) { /* SAPI0 on FACCH or SDCCH */ link_id := ts_RslLinkID_DCCH(0); dlci := '80'O; } else { /* SAPI3 on SACCH */ link_id := ts_RslLinkID_SACCH(3); dlci := 'C3'O; } /* Send MO message: RSL -> BSSAP */ f_mo_l3_transceive(RSL, link_id, dlci, l3); /* Send MT message: BSSAP -> RSL */ f_mt_l3_transceive(RSL, link_id, dlci, l3); } } testcase TC_tch_dlci_link_id_sapi() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_tch_dlci_link_id_sapi), pars); vc_conn.done; f_shutdown_helper(); } private function f_exp_sapi_n_reject(template (present) GsmSapi sapi := ?, template myBSSMAP_Cause cause := ?, float T_val := 2.0) runs on test_CT { var BSSAP_N_DATA_ind rx_di; timer T; var template BSSMAP_IE_Cause tr_cause := tr_BSSMAP_IE_Cause(cause); var template PDU_BSSAP tr_pdu := tr_BSSMAP_SAPInReject(sapi); T.start(T_val); alt { [] BSSAP.receive(tr_BSSAP_DATA_ind(?, tr_pdu)) -> value rx_di { var BSSMAP_IE_Cause rx_cause := rx_di.userData.pdu.bssmap.sAPInReject.cause; if (not match(rx_cause, tr_cause)) { setverdict(fail, "Rx unexpected Cause IE: ", rx_cause, " vs expected ", tr_cause); } setverdict(pass); } [] BSSAP.receive(BSSAP_N_DATA_ind:?) -> value rx_di { setverdict(fail, "Rx unexpected BSSAP PDU: ", rx_di); } [] T.timeout { setverdict(fail, "Timeout waiting for BSSMAP SAPI N Reject"); } } } /* Check if we get SAPI N Reject on receipt of unexpected RLL RELease INDication */ testcase TC_rll_rel_ind_sapi_n_reject() runs on test_CT { var octetstring rnd_data := f_rnd_octstring(16); var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); /* MS establishes a SAPI=0 link on DCCH */ dt := f_est_dchan(f_rnd_ra_cs(), 23, rnd_data); /* MSC sends some data on (not yet established) SAPI=3 link */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(rnd_data, '03'O))); /* BSC attempts to establish a SAPI=3 link on DCCH */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3))); /* MS sends unexpected RELease INDication on SAPI=3 */ f_ipa_tx(0, ts_RSL_REL_IND(dt.rsl_chan_nr, ts_RslLinkID_DCCH(3))); /* We expect to receive BSSMAP SAPI N Reject message from the BSC */ f_exp_sapi_n_reject(3, GSM0808_CAUSE_MS_NOT_EQUIPPED); /* Clean up the connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Check if we get SAPI N Reject on receipt of unexpected RLL ERROR INDication */ testcase TC_rll_err_ind_sapi_n_reject() runs on test_CT { var octetstring rnd_data := f_rnd_octstring(16); var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); /* MS establishes a SAPI=0 link on DCCH */ dt := f_est_dchan(f_rnd_ra_cs(), 23, rnd_data); /* MSC sends some data on (not yet established) SAPI=3 link */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(rnd_data, '03'O))); /* BSC attempts to establish a SAPI=3 link on DCCH */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3))); /* BTS sends unexpected ERROR INDication on SAPI=3 */ f_ipa_tx(0, ts_RSL_ERROR_IND(dt.rsl_chan_nr, ts_RslLinkID_DCCH(3), ''O)); /* We expect to receive BSSMAP SAPI N Reject message from the BSC */ f_exp_sapi_n_reject(3, GSM0808_CAUSE_BSS_NOT_EQUIPPED); /* Clean up the connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } /* Check if we get SAPI N Reject due to a SAPI=3 link establishment timeout */ testcase TC_rll_timeout_sapi_n_reject() runs on test_CT { var octetstring rnd_data := f_rnd_octstring(16); var RSL_Message rx_rsl; var DchanTuple dt; f_init(1); /* MS establishes a SAPI=0 link on DCCH */ dt := f_est_dchan(f_rnd_ra_cs(), 23, rnd_data); /* MSC sends some data on (not yet established) SAPI=3 link */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(rnd_data, '03'O))); /* BSC attempts to establish a SAPI=3 link on DCCH */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3))); /* MS does not respond, so the link establishment timeout triggers SAPI N Reject */ f_exp_sapi_n_reject(3, GSM0808_CAUSE_BSS_NOT_EQUIPPED, T_val := 8.0); /* Clean up the connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false); f_shutdown_helper(); } testcase TC_si_default() runs on test_CT { f_init(0); f_init_bts_and_check_sysinfo(0, expect_si := SystemInformationConfig_default); f_shutdown_helper(); } /* We're testing SI2quater with lists of EARFCNs. Instead of just incrementing EARFCNs, also pick some from the edges of * the entire value range. This function provides the same EARFCN numbers for the same earfcn_index */ private function f_test_si2quater_earfcn_by_idx(integer earfcn_index) return uint16_t { select (earfcn_index) { case (0) { /* E-ARFCN 111 is already added in the osmo-bsc.cfg */ return 111; } case (1) { return 1; } case (2) { return 0; } case (3) { return 65535; } case else { return 23 * (earfcn_index - 3); } } } function f_test_si2quater(integer total_earfcns, template SystemInformationConfig expect_si, template CellSelIndValue expect_cells := omit) runs on test_CT { f_init(0); /* E-ARFCN 111 is already added in the osmo-bsc.cfg, so only add more arfcns if total_earfcns > 1 */ for (var integer i := 1; i < total_earfcns; i := i + 1) { f_bts_0_cfg(BSCVTY, {"si2quater neighbor-list add earfcn " & int2str(f_test_si2quater_earfcn_by_idx(i)) & " thresh-hi 20 thresh-lo 10 prio 3 qrxlv 22 meas 3"}); } f_init_bts_and_check_sysinfo(0, expect_si := expect_si); f_init(1); if (not istemplatekind(expect_cells, "omit")) { /* Also check that RR Channel Release contains these EARFCNs. * (copied code from TC_chan_rel_hard_clear_csfb) */ var BSSAP_N_DATA_ind rx_di; var DchanTuple dt; dt := f_est_dchan('23'O, 23, '00010203040506'O); /* Instruct BSC to clear channel */ var BssmapCause cause := 0; BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommandCSFB(cause))); /* expect Clear Complete from BSC on A */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearComplete)) { /* release the SCCP connection */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } f_expect_chan_rel(0, dt.rsl_chan_nr, expect_rll_rel_req := false, is_csfb := true, csfb_expect_cells := expect_cells); } for (var integer i := 1; i < total_earfcns; i := i + 1) { f_bts_0_cfg(BSCVTY, {"si2quater neighbor-list del earfcn " & int2str(f_test_si2quater_earfcn_by_idx(i))}); } } private function f_tr_si2quater_earfcns(integer count) return template SI2quaterRestOctetsList { var template SI2quaterRestOctetsList si2quater := {}; var integer si2quater_count := (count + 2) / 3; for (var integer i := 0; i < count; i := i + 1) { var integer earfcn := f_test_si2quater_earfcn_by_idx(i); var integer index := i / 3; var integer earfcn_index := i mod 3; if (index >= lengthof(si2quater)) { si2quater[index] := tr_SI2quaterRestOctets_EUTRAN(index := index, count := si2quater_count - 1); } si2quater[index].rel_additions.rel5.rel6.rel7.rel8.prio_eutran_params_desc.desc.eutran_params_desc.desc.repeated_neigh_cells[0].cell_desc_list[earfcn_index] := tr_EUTRAN_CellDesc_default(e_arfcn := earfcn); } return si2quater; } private function f_tr_rr_chan_rel_earfcns(integer count) return template CellSelIndValue { var template CellSelIndValue_EUTRAN_Descrs cells := {}; /* the lte neighbors must match the config & vty to pass this test */ for (var integer i := 0; i < count; i := i + 1) { var integer earfcn := f_test_si2quater_earfcn_by_idx(i); cells[i] := tr_CellSelIndValue_EUTRAN_Descr(earfcn, '1'B, 3); } return tr_CellSelIndValue_EUTRAN(cells); } private function f_tc_si2quater_n_earfcns(integer n) runs on test_CT { var template SystemInformationConfig sic := SystemInformationConfig_default; sic.si2quater := f_tr_si2quater_earfcns(n); var template CellSelIndValue cells := f_tr_rr_chan_rel_earfcns(n); f_test_si2quater(n, sic, cells); } testcase TC_si2quater_2_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(2); f_shutdown_helper(); } testcase TC_si2quater_3_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(3); f_shutdown_helper(); } testcase TC_si2quater_4_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(4); f_shutdown_helper(); } testcase TC_si2quater_5_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(5); f_shutdown_helper(); } testcase TC_si2quater_6_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(6); f_shutdown_helper(); } testcase TC_si2quater_12_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(12); f_shutdown_helper(); } testcase TC_si2quater_23_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(23); f_shutdown_helper(); } testcase TC_si2quater_32_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(32); f_shutdown_helper(); } testcase TC_si2quater_33_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(33); f_shutdown_helper(); } testcase TC_si2quater_42_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(42); f_shutdown_helper(); } testcase TC_si2quater_48_earfcns() runs on test_CT { f_tc_si2quater_n_earfcns(48); f_shutdown_helper(); } /* verify the VTY error response when adding too many EARFCNs, and showing that osmo-bsc still sends 16 SI2quater with * 48 EARFCNs. */ testcase TC_si2quater_49_earfcns() runs on test_CT { var template SystemInformationConfig sic := SystemInformationConfig_default; sic.si2quater := f_tr_si2quater_earfcns(48); /* 48, not 49! */ f_init(0); for (var integer i := 1; i < 48; i := i + 1) { f_bts_0_cfg(BSCVTY, {"si2quater neighbor-list add earfcn " & int2str(f_test_si2quater_earfcn_by_idx(i)) & " thresh-hi 20 thresh-lo 10 prio 3 qrxlv 22 meas 3"}); } /* The 49th EARFCN no longer fits, expect VTY error */ f_vty_enter_cfg_bts(BSCVTY, 0); var charstring vty_error; vty_error := f_vty_transceive_ret(BSCVTY, "si2quater neighbor-list add earfcn 70 thresh-hi 20 thresh-lo 10 prio 3 qrxlv 22 meas 3") f_vty_transceive(BSCVTY, "end"); if (f_strstr(vty_error, "Unable to add ARFCN 70") >= 0) { log("Got expected VTY error: ", vty_error); setverdict(pass); } else { setverdict(fail, "Expected the 49th EUTRAN ARFCN to be rejected by vty config, got: ", vty_error); } f_init_bts_and_check_sysinfo(0, expect_si := sic); for (var integer i := 1; i < 48; i := i + 1) { f_bts_0_cfg(BSCVTY, {"si2quater neighbor-list del earfcn " & int2str(f_test_si2quater_earfcn_by_idx(i))}); } f_shutdown_helper(); } private function f_acc09_count_allowed(AccessControlClass acc) return uint8_t { var uint8_t count := 0; for (var integer i := 5; i < 16; i := i + 1) { if (acc[i] == '0'B) { /* the list marks barred, we count allowed */ count := count + 1; } } return count; } private function f_recv_next_si1(integer rsl_idx := 0) runs on test_CT return SystemInformationType1 { var ASP_RSL_Unitdata rx_rsl_ud; var SystemInformationType1 last_si1; timer T := 30.0; T.start; alt { [] IPA_RSL[rsl_idx].receive(tr_ASP_RSL_UD((tr_RSL_NO_BCCH_INFO, tr_RSL_BCCH_INFO, tr_RSL_NO_SACCH_FILL, tr_RSL_SACCH_FILL)) ) -> value rx_rsl_ud { f_sysinfo_seen(rsl_idx, rx_rsl_ud.rsl); if (g_system_information[rsl_idx].si1 == omit) { repeat; } last_si1 := g_system_information[rsl_idx].si1; g_system_information[rsl_idx].si1 := omit; T.stop; } [] T.timeout { setverdict(fail, "Timeout receiving next SI1"); } } return last_si1; } /* verify ACC rotate feature */ testcase TC_si_acc_rotate() runs on test_CT { var template SystemInformationConfig sic := SystemInformationConfig_default; var SystemInformationType1 last_si1; var AccessControlClass acc; var uint8_t count; var integer times_allowed[10] := { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; f_init(0, guard_timeout := 60.0); f_bts_0_cfg(BSCVTY, {"rach access-control-class 5 barred", "access-control-class-rotate 3", "access-control-class-rotate-quantum 1"}); /* Init and get first sysinfo */ f_init_bts_and_check_sysinfo(0, expect_si := ?); for (var integer i:= 0; i < 20; i := i + 1) { last_si1 := f_recv_next_si1(0); acc := last_si1.rach_control.acc; count := f_acc09_count_allowed(acc); log("RSL: GOT SI1 ACC len=", count, ": ", acc); if (count != 3) { log("RSL: EXPECTED SI ACC len=3"); setverdict(fail, "received SI does not match expectations"); break; } for (var integer j := 0; j < 10; j := j + 1) { if (acc[16 - 1 - j] == '0'B) { /* the list marks barred, we count allowed */ times_allowed[j] := times_allowed[j] + 1; } } } for (var integer j := 0; j < 10; j := j + 1) { log("ACC", j, " allowed ", times_allowed[j], " times" ); if (j != 5 and times_allowed[j] < 3) { setverdict(fail, "ACC", j, " ERROR: allowed ", times_allowed[j], " < 1 times"); } else if (j == 5 and times_allowed[j] > 0) { setverdict(fail, "ACC", j, " ERROR: allowed ", times_allowed[j], " > 0 times"); } } f_bts_0_cfg(BSCVTY, {"access-control-class-rotate 10", "rach access-control-class 5 allowed"}); f_shutdown_helper(); } /* verify ACC startup ramp+rotate feature */ testcase TC_si_acc_ramp_rotate() runs on test_CT { var template SystemInformationConfig sic := SystemInformationConfig_default; var SystemInformationType1 last_si1; var AccessControlClass acc; var ASP_RSL_Unitdata rx_rsl_ud; var uint8_t count; var uint8_t prev_count; var integer times_allowed[10] := { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; f_init(0, guard_timeout := 80.0); f_bts_0_cfg(BSCVTY, {"rach access-control-class 4 barred", "access-control-class-rotate 0", "access-control-class-rotate-quantum 1", "access-control-class-ramping", "access-control-class-ramping-step-interval 5", "access-control-class-ramping-step-size 5"}); /* Init and get first sysinfo */ f_init_bts_and_check_sysinfo(0, expect_si := ?); last_si1 := g_system_information[0].si1; acc := last_si1.rach_control.acc; count := f_acc09_count_allowed(acc); /* Adm subset size was set to 0 above, so wait until all ACC are barred */ while (count > 0) { last_si1 := f_recv_next_si1(0); acc := last_si1.rach_control.acc; count := f_acc09_count_allowed(acc); log("RSL: wait len()=0: GOT SI1 ACC len=", count, ": ", acc); } /* Increase adm subset size, we should see ramping start up */ f_bts_0_cfg(BSCVTY, {"access-control-class-rotate 10"}); prev_count := 0; while (true) { last_si1 := f_recv_next_si1(0); acc := last_si1.rach_control.acc; count := f_acc09_count_allowed(acc); log("RSL: GOT SI1 ACC len=", count, ": ", acc); if (prev_count > count) { setverdict(fail, "ACC allowed count dropped while expecting grow: ", prev_count, " -> ", count); break; } if (count == 9) { break; /* Maximum reached (10 - 1 perm barred), done here */ } prev_count := count; } setverdict(pass); f_bts_0_cfg(BSCVTY, {"access-control-class-rotate 10", "rach access-control-class 4 allowed", "no access-control-class-ramping"}); f_shutdown_helper(); } testcase TC_ctrl_msc_connection_status() runs on test_CT { var charstring ctrl_resp; f_init(1); /* See https://osmocom.org/issues/2729 */ f_ctrl_get_exp(IPA_CTRL, "msc_connection_status", "connected"); f_shutdown_helper(); } testcase TC_ctrl_msc0_connection_status() runs on test_CT { var charstring ctrl_resp; f_init(1); f_ctrl_get_exp(IPA_CTRL, "msc.0.connection_status", "connected"); f_shutdown_helper(); } testcase TC_ctrl() runs on test_CT { var charstring ctrl_resp; f_init(1); /* all below values must match the osmo-bsc.cfg config file used */ f_ctrl_get_exp(IPA_CTRL, "mcc", "001"); f_ctrl_get_exp(IPA_CTRL, "mnc", "01"); f_ctrl_get_exp(IPA_CTRL, "number-of-bts", "4"); var integer bts_nr := 0; f_ctrl_get_exp_bts(IPA_CTRL, bts_nr, "location-area-code", "1"); f_ctrl_get_exp_bts(IPA_CTRL, bts_nr, "cell-identity", "0"); f_ctrl_get_exp_bts(IPA_CTRL, bts_nr, "oml-connection-state", "connected"); f_ctrl_get_exp_bts(IPA_CTRL, bts_nr, "gprs-mode", "gprs"); f_ctrl_get_exp_bts(IPA_CTRL, bts_nr, "rf_state", "operational,unlocked,on"); f_ctrl_get_exp_trx(IPA_CTRL, bts_nr, 0, "arfcn", "871"); f_ctrl_get_exp_trx(IPA_CTRL, bts_nr, 0, "max-power-reduction", "20"); var integer uptime := str2int(f_ctrl_get_bts(IPA_CTRL, bts_nr, "oml-uptime")); f_sleep(2.0); if (str2int(f_ctrl_get_bts(IPA_CTRL, bts_nr, "oml-uptime")) < uptime+1) { setverdict(fail, "oml-uptime not incrementing as expected"); } /* TODO: Disconnect RSL, imply that OML is disconnected and check for uptime zero? */ f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bsc", 0, "paging:attempted", 0); f_shutdown_helper(); } /* Verify that Upon receival of SET "location", BSC forwards a TRAP "location-state" over the SCCPlite IPA conn */ testcase TC_ctrl_location() runs on test_CT { var MSC_ConnHdlr vc_conn; var integer bts_nr := 0; f_init(1, true); f_sleep(1.0); f_ctrl_set_bts(IPA_CTRL, bts_nr, "location", "1234567,fix3d,0.340000,0.560000,0.780000"); f_ctrl_exp_trap(SCCPLITE_IPA_CTRL, "bts." & int2str(bts_nr) & ".location-state", "1234567,fix3d,0.340000,0.560000,0.780000,operational,unlocked,on,001,01"); f_ctrl_set(SCCPLITE_IPA_CTRL, "rf_locked", "1"); f_sleep(2.0); f_ctrl_set_bts(IPA_CTRL, bts_nr, "location", "1234888,fix3d,0.350000,0.570000,0.790000"); f_ctrl_exp_trap(SCCPLITE_IPA_CTRL, "bts." & int2str(bts_nr) & ".location-state", "1234888,fix3d,0.350000,0.570000,0.790000,operational,locked,off,001,01"); /* should match the one from config */ f_ctrl_set(SCCPLITE_IPA_CTRL, "rf_locked", "0"); f_shutdown_helper(); } /*********************************************************************** * Paging Testing ***********************************************************************/ type record Cell_Identity { GsmMcc mcc, GsmMnc mnc, GsmLac lac, GsmCellId ci }; private const Cell_Identity cid := { '001'H, '01'H, 1, 0 }; private const Cell_Identity unknown_cid := { '678'H, 'f90'H, 1, 0 }; type set of integer BtsIdList; private function f_bts_in_list(integer bts_id, BtsIdList bts_ids) return boolean { for (var integer j := 0; j < sizeof(bts_ids); j := j + 1) { if (bts_id == bts_ids[j]) { return true; } } return false; } /* core paging test helper function; used by most paging test cases */ private function f_pageing_helper(hexstring imsi, template BSSMAP_FIELD_CellIdentificationList cid_list, BtsIdList bts_ids := { 0 }, template RSL_ChanNeeded rsl_chneed := omit, template (omit) OCT4 tmsi := omit) runs on test_CT { var template BSSMAP_IE_ChannelNeeded bssmap_chneed; var template MobileIdentityV mi; var RSL_Message rx_rsl; var integer paging_group := hex2int(imsi[lengthof(imsi)-1]); var integer i; f_init(); /* Clear the queue, it might still contain stuff like BCCH FILLING */ for (i := 0; i < NUM_BTS; i := i + 1) { IPA_RSL[i].clear; } if (isvalue(rsl_chneed)) { /* The values of 08.08 3.2.2.36 and 08.58 9.3.40 are luckily identical */ bssmap_chneed := ts_BSSMAP_IE_ChanNeeded(int2bit(enum2int(valueof(rsl_chneed)),2)); } else { bssmap_chneed := omit; } BSSAP.send(ts_BSSAP_UNITDATA_req(g_bssap[0].sccp_addr_peer, g_bssap[0].sccp_addr_own, ts_BSSMAP_Paging(imsi, cid_list, tmsi, bssmap_chneed))); if (not istemplatekind(tmsi, "omit")) { mi := t_MI_TMSI(tmsi); } else { mi := tr_MI_IMSI(imsi); } for (i := 0; i < sizeof(bts_ids); i := i + 1) { rx_rsl := f_exp_ipa_rx(bts_ids[i], tr_RSL_PAGING_CMD(mi)); /* check channel type, paging group */ if (rx_rsl.ies[1].body.paging_group != paging_group) { setverdict(fail, "Paging for wrong paging group"); } if (ispresent(rsl_chneed) and rx_rsl.ies[3].body.chan_needed.chan_needed != valueof(rsl_chneed)) { setverdict(fail, "RSL Channel Needed != BSSMAP Channel Needed"); } } f_sleep(2.0); /* do a quick check on all not-included BTSs if they received paging */ for (i := 0; i < NUM_BTS; i := i + 1) { timer T := 0.1; if (f_bts_in_list(i, bts_ids)) { continue; } T.start; alt { [] IPA_RSL[i].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(mi))) { setverdict(fail, "Paging on BTS ", i, " which is not part of ", bts_ids); } [] IPA_RSL[i].receive { repeat; } [] T.timeout { } } } setverdict(pass); } const BtsIdList c_BtsId_all := { 0, 1, 2 }; const BtsIdList c_BtsId_none := { }; const BtsIdList c_BtsId_LAC1 := { 0, 1 }; const BtsIdList c_BtsId_LAC2 := { 2 }; /* PAGING by IMSI + TMSI */ testcase TC_paging_imsi_nochan() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010100000001'H, cid_list, c_BtsId_all, omit, omit); f_shutdown_helper(); } /* PAGING by IMSI + TMSI */ testcase TC_paging_tmsi_nochan() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010100000001'H, cid_list, c_BtsId_all, omit, 'A1B2C301'O); f_shutdown_helper(); } /* Paging with different "channel needed' values */ testcase TC_paging_tmsi_any() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010100000002'H, cid_list, c_BtsId_all, RSL_CHANNEED_ANY, 'A1B2C302'O); f_shutdown_helper(); } testcase TC_paging_tmsi_sdcch() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010100000003'H, cid_list, c_BtsId_all, RSL_CHANNEED_SDCCH, 'A1B2C303'O); f_shutdown_helper(); } testcase TC_paging_tmsi_tch_f() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010000000004'H, cid_list, c_BtsId_all, RSL_CHANNEED_TCH_F, 'A1B2C304'O); f_shutdown_helper(); } testcase TC_paging_tmsi_tch_hf() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010000000005'H, cid_list, c_BtsId_all, RSL_CHANNEED_TCH_ForH, 'A1B2C305'O); f_shutdown_helper(); } /* Paging by CGI */ testcase TC_paging_imsi_nochan_cgi() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_CGI := { ts_BSSMAP_CI_CGI(cid.mcc, cid.mnc, cid.lac, cid.ci) } }; f_pageing_helper('001010000000006'H, cid_list, { 0 }); f_shutdown_helper(); } /* Paging by LAC+CI */ testcase TC_paging_imsi_nochan_lac_ci() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAC_CI := { ts_BSSMAP_CI_LAC_CI(cid.lac, cid.ci) } }; f_pageing_helper('001010000000007'H, cid_list, { 0 }); f_shutdown_helper(); } /* Paging by CI */ testcase TC_paging_imsi_nochan_ci() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_CI := { ts_BSSMAP_CI_CI(cid.ci) } }; f_pageing_helper('001010000000008'H, cid_list, { 0 }); f_shutdown_helper(); } /* Paging by LAI */ testcase TC_paging_imsi_nochan_lai() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAI := { ts_BSSMAP_CI_LAI(cid.mcc, cid.mnc, cid.lac) } }; f_pageing_helper('001010000000009'H, cid_list, c_BtsId_LAC1); f_shutdown_helper(); } /* Paging by LAC */ testcase TC_paging_imsi_nochan_lac() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAC := { ts_BSSMAP_CI_LAC(cid.lac) } }; f_pageing_helper('001010000000010'H, cid_list, c_BtsId_LAC1); f_shutdown_helper(); } /* Paging by "all in BSS" */ testcase TC_paging_imsi_nochan_all() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_allInBSS := ''O }; f_pageing_helper('001010000000011'H, cid_list, c_BtsId_all); f_shutdown_helper(); } /* Paging by PLMN+LAC+RNC; We do not implement this; Verify nothing is paged */ testcase TC_paging_imsi_nochan_plmn_lac_rnc() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_PLMN_LAC_RNC := { ts_BSSMAP_CI_PLMN_LAC_RNC(cid.mcc, cid.mnc, cid.lac, 12) } }; f_pageing_helper('001010000000012'H, cid_list, c_BtsId_none); f_shutdown_helper(); } /* Paging by RNC; We do not implement this; Verify nothing is paged */ testcase TC_paging_imsi_nochan_rnc() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_RNC := { int2oct(13, 2) } }; f_pageing_helper('001010000000013'H, cid_list, c_BtsId_none); f_shutdown_helper(); } /* Paging by LAC+RNC; We do not implement; Verify nothing is paged */ testcase TC_paging_imsi_nochan_lac_rnc() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAC_RNC := { ts_BSSMAP_CI_LAC_RNC(cid.lac, 14) } }; f_pageing_helper('001010000000014'H, cid_list, c_BtsId_none); f_shutdown_helper(); } /* Paging on multiple cells (multiple entries in list): Verify all of them page */ testcase TC_paging_imsi_nochan_lacs() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAC := { ts_BSSMAP_CI_LAC(1), ts_BSSMAP_CI_LAC(2) } }; f_pageing_helper('001010000000015'H, cid_list, c_BtsId_all); f_shutdown_helper(); } /* Paging on empty list: Verify none of them page */ testcase TC_paging_imsi_nochan_lacs_empty() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_LAC := { } }; f_pageing_helper('001010000000016'H, cid_list, c_BtsId_none); f_shutdown_helper(); } /* Paging by CGI with unknown MCC/MNC: Verify nothing is paged. */ testcase TC_paging_imsi_nochan_cgi_unknown_cid() runs on test_CT { var template BSSMAP_FIELD_CellIdentificationList cid_list; cid_list := { cIl_CGI := { ts_BSSMAP_CI_CGI(unknown_cid.mcc, unknown_cid.mnc, unknown_cid.lac, unknown_cid.ci) } }; f_pageing_helper('001010000000006'H, cid_list, c_BtsId_none); f_shutdown_helper(); } /* Verify paging retransmission interval + count */ /* Verify paging stops after channel establishment */ /* Test behavior under paging overload */ /* Verify PCH load */ testcase TC_paging_imsi_load() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; timer T := 4.0; timer T_retrans := 1.0; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010123456789'H, cid_list, c_BtsId_all); /* tell BSC there is no paging space anymore */ f_ipa_tx(0, ts_RSL_PAGING_LOAD_IND(0)); f_sleep(0.2); IPA_RSL[0].clear; /* Wait for 4 seconds if any more PAGING CMD are received on RSL. Normally, * there would be 8 retransmissions during 4 seconds */ T.start; T_retrans.start; alt { [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) { setverdict(fail, "Received PAGING after LOAD_IND(0)"); mtc.stop; } [] T_retrans.timeout { /* re-trnsmit the zero-space LOAD IND to avoid BSC 'auto credit' */ f_ipa_tx(0, ts_RSL_PAGING_LOAD_IND(0)); T_retrans.start; repeat; } [] T.timeout { setverdict(pass); } } f_shutdown_helper(); } /* Verify Paging Counter */ testcase TC_paging_counter() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; timer T := 4.0; var integer i; var integer paging_attempted_bsc; var integer paging_attempted_bts[NUM_BTS]; var integer paging_expired_bts[NUM_BTS]; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_init(); /* read counters before paging */ paging_attempted_bsc := f_ctrl_get_ratectr_abs(IPA_CTRL, "bsc", 0, "paging:attempted"); for (i := 0; i < NUM_BTS; i := i+1) { paging_attempted_bts[i] := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", i, "paging:attempted"); paging_expired_bts[i] := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", i, "paging:expired"); } f_pageing_helper('001230000000001'H, cid_list, c_BtsId_all); /* expect the attempted pages on BSC and each BTSs to have incremented by one */ f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bsc", 0, "paging:attempted", paging_attempted_bsc+1); for (i := 0; i < NUM_BTS; i := i+1) { f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", i, "paging:attempted", paging_attempted_bts[i]+1); } /* assume that 12s later the paging on all BTSs have expired and hence incremented by 1 */ f_sleep(12.0); for (i := 0; i < NUM_BTS; i := i+1) { f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", i, "paging:expired", paging_expired_bts[i]+1); } f_shutdown_helper(); } /* Verify paging stops after A-RESET */ testcase TC_paging_imsi_a_reset() runs on test_CT { var BSSMAP_FIELD_CellIdentificationList cid_list; timer T := 3.0; cid_list := valueof(ts_BSSMAP_CIL_noCell); f_pageing_helper('001010123456789'H, cid_list, c_BtsId_all); /* Perform a BSSMAP Reset and wait for ACK */ BSSAP.send(ts_BSSAP_UNITDATA_req(g_bssap[0].sccp_addr_peer, g_bssap[0].sccp_addr_own, ts_BSSMAP_Reset(0, g_osmux_enabled))); alt { [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(g_bssap[0].sccp_addr_own, g_bssap[0].sccp_addr_peer, tr_BSSMAP_ResetAck(g_osmux_enabled))) { } [] BSSAP.receive { repeat; } } /* Wait to avoid a possible race condition if a paging message is * received right before the reset ACK. */ f_sleep(0.2); /* Clear the queue, it might still contain stuff like BCCH FILLING */ for (var integer i := 0; i < sizeof(IPA_RSL); i := i+1) { IPA_RSL[i].clear; } /* Wait for 3 seconds if any more PAGING CMD are received on RSL */ T.start; alt { [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) { setverdict(fail, "Received PAGING after A-RESET"); mtc.stop; } [] IPA_RSL[1].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) { setverdict(fail, "Received PAGING after A-RESET"); mtc.stop; } [] IPA_RSL[2].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) { setverdict(fail, "Received PAGING after A-RESET"); mtc.stop; } [] T.timeout { setverdict(pass); } } f_shutdown_helper(); } /* Verify how we handle unsolicited Paging Response. In case of an unsolicit * paging response we can not know which MSC is in charge, so we will blindly * pick the first configured MSC. This behavior is required in order to make * MT-CSFB calls working because in those cases the BSC can not know that the * MSC has already paged the subscriver via SGs. So any MT-CSFB call will look * like an unsolicited Paging Response to the MSC. */ testcase TC_paging_resp_unsol() runs on test_CT { f_init(1); timer T := 5.0; var BSSAP_N_CONNECT_ind rx_c_ind; var DchanTuple dt; var PDU_ML3_MS_NW l3 := valueof(ts_PAG_RESP(valueof(ts_MI_IMSI_LV('001010008880018'H)))); var octetstring rr_pag_resp := enc_PDU_ML3_MS_NW(l3); /* Send CHAN RQD and wait for allocation; acknowledge it */ dt.rsl_chan_nr := f_chreq_act_ack(); /* Send unsolicited Paging response (no matching Paging CMD stored in BSC) */ f_ipa_tx(0, ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), enc_PDU_ML3_MS_NW(l3))); /* Expevct a CR with a matching Paging response on the A-Interface */ T.start; alt { [] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(rr_pag_resp))) { setverdict(pass); } [] BSSAP.receive { setverdict(fail, "Received unexpected message on A-Interface!"); } [] T.timeout { setverdict(fail, "Received nothing on A-Interface!"); } } f_shutdown_helper(); } /* Test RSL link drop causes counter increment */ testcase TC_rsl_drop_counter() runs on test_CT { var integer rsl_fail; f_init(1); rsl_fail := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "rsl_fail"); bts[0].rsl.vc_IPA.stop; f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "rsl_fail", rsl_fail+1); f_shutdown_helper(); } /* TODO: Test OML link drop causes counter increment */ /* The body of TC_rsl_unknown_unit_id() and TC_oml_unknown_unit_id() tests. */ function f_ipa_unknown_unit_id(integer mp_bsc_ipa_port) runs on test_CT return boolean { timer T := 10.0; bts[0].rsl.id := "IPA-0-RSL"; bts[0].rsl.vc_IPA := IPA_Emulation_CT.create(bts[0].rsl.id & "-IPA"); bts[0].rsl.ccm_pars := c_IPA_default_ccm_pars; bts[0].rsl.ccm_pars.name := "Osmocom TTCN-3 BTS Simulator"; bts[0].rsl.ccm_pars.unit_id := "99/0/0"; /* value which is unknown at BTS */ f_ipa_ctrl_start(mp_bsc_ip, mp_bsc_ctrl_port); f_init_mgcp("VirtMSC"); /* start RSL/OML connection (XXX re-uses RSL port/protocol definitions for OML) */ map(bts[0].rsl.vc_IPA:IPA_PORT, system:IPA); connect(bts[0].rsl.vc_IPA:IPA_RSL_PORT, self:IPA_RSL[0]); bts[0].rsl.vc_IPA.start(IPA_Emulation.main_client(mp_bsc_ip, mp_bsc_ipa_port, "", 10000, bts[0].rsl.ccm_pars)); /* wait for IPA OML link to connect and then disconnect */ T.start; alt { [] IPA_RSL[0].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) { T.stop; return true; } [] IPA_RSL[0].receive { repeat } [] T.timeout { return false; } } return false; } /* BSC should close an RSL connection from a BTS with unknown unit ID (OS#2714). */ testcase TC_rsl_unknown_unit_id() runs on test_CT { if (f_ipa_unknown_unit_id(mp_bsc_rsl_port)) { setverdict(pass); } else { setverdict(fail, "Timeout RSL waiting for connection to close"); } f_shutdown_helper(); } /* BSC should close an RSL connection from a BTS with unknown unit ID (OS#2714). */ testcase TC_oml_unknown_unit_id() runs on test_CT { if (f_ipa_unknown_unit_id(mp_bsc_oml_port)) { setverdict(pass); } else { setverdict(fail, "Timeout OML waiting for connection to close"); } f_shutdown_helper(); } /*********************************************************************** * "New world" test cases using RSL_Emulation + RAN_Emulation ***********************************************************************/ import from RAN_Emulation all; import from BSSAP_LE_Emulation all; import from RSL_Emulation all; import from MSC_ConnectionHandler all; type function void_fn(charstring id) runs on MSC_ConnHdlr; /* helper function to create and connect a MSC_ConnHdlr component */ private function f_connect_handler(inout MSC_ConnHdlr vc_conn, integer bssap_idx := 0) runs on test_CT { connect(vc_conn:RAN, g_bssap[bssap_idx].vc_RAN:PROC); connect(vc_conn:MGCP_PROC, vc_MGCP:MGCP_PROC); connect(vc_conn:RSL, bts[0].rsl.vc_RSL:CLIENT_PT); connect(vc_conn:RSL_PROC, bts[0].rsl.vc_RSL:RSL_PROC); if (isvalue(bts[1])) { connect(vc_conn:RSL1, bts[1].rsl.vc_RSL:CLIENT_PT); connect(vc_conn:RSL1_PROC, bts[1].rsl.vc_RSL:RSL_PROC); } if (isvalue(bts[2])) { connect(vc_conn:RSL2, bts[2].rsl.vc_RSL:CLIENT_PT); connect(vc_conn:RSL2_PROC, bts[2].rsl.vc_RSL:RSL_PROC); } connect(vc_conn:BSSAP, g_bssap[bssap_idx].vc_RAN:CLIENT); if (mp_enable_lcs_tests) { connect(vc_conn:BSSAP_LE, g_bssap_le.vc_BSSAP_LE:CLIENT); connect(vc_conn:BSSAP_LE_PROC, g_bssap_le.vc_BSSAP_LE:PROC); } connect(vc_conn:MGCP, vc_MGCP:MGCP_CLIENT); connect(vc_conn:MGCP_MULTI, vc_MGCP:MGCP_CLIENT_MULTI); connect(vc_conn:STATSD_PROC, vc_STATSD:STATSD_PROC); } function f_start_handler(void_fn fn, template (omit) TestHdlrParams pars := omit) runs on test_CT return MSC_ConnHdlr { var charstring id := testcasename(); var MSC_ConnHdlr vc_conn; var integer bssap_idx := 0; if (isvalue(pars)) { bssap_idx := valueof(pars).mscpool.bssap_idx; } vc_conn := MSC_ConnHdlr.create(id); f_connect_handler(vc_conn, bssap_idx); /* Emit a marker to appear in the SUT's own logging output */ f_logp(BSCVTY, testcasename() & "() start"); vc_conn.start(f_handler_init(fn, id, pars)); return vc_conn; } /* first function inside ConnHdlr component; sets g_pars + starts function */ private function f_handler_init(void_fn fn, charstring id, template (omit) TestHdlrParams pars := omit) runs on MSC_ConnHdlr { if (isvalue(pars)) { g_pars := valueof(pars); } fn.apply(id); } /* Establish signalling channel (non-assignment case) followed by cipher mode */ private function f_tc_ciph_mode_a5(charstring id) runs on MSC_ConnHdlr { var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeSIGNAL); ass_cmd.pdu.bssmap.assignmentRequest.circuitIdentityCode := omit; ass_cmd.pdu.bssmap.assignmentRequest.aoIPTransportLayer := omit; exp_compl.pdu.bssmap.assignmentComplete.circuitIdentityCode := omit; exp_compl.pdu.bssmap.assignmentComplete.aoIPTransportLayer := omit; f_establish_fully(ass_cmd, exp_compl); } testcase TC_ciph_mode_a5_0() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('01'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_ciph_mode_a5), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_ciph_mode_a5_1() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('02'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_ciph_mode_a5), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_ciph_mode_a5_3() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('08'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_ciph_mode_a5), pars); vc_conn.done; f_shutdown_helper(); } /* establish initial channel, enable ciphering followed by assignment to ciphered channel */ private function f_tc_assignment_aoip_tla_v6(charstring id) runs on MSC_ConnHdlr { var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(aoip_tla := "::3"); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); } testcase TC_assignment_aoip_tla_v6() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_aoip_tla_v6), pars); vc_conn.done; f_shutdown_helper(); } /* establish initial channel, enable ciphering followed by assignment to ciphered channel */ private function f_tc_assignment_fr_a5(charstring id) runs on MSC_ConnHdlr { var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); } testcase TC_assignment_fr_a5_0() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('01'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_fr_a5_1() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('02'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_fr_a5_3() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.encr := valueof(t_EncrParams('08'O, f_rnd_octstring(8))); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5), pars); vc_conn.done; f_shutdown_helper(); } /* Expect ASSIGNMENT FAIL if mandatory IE is missing */ private function f_tc_assignment_fr_a5_1_codec_missing(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; var PDU_BSSAP ass_cmd := f_gen_ass_req(); const OCT8 kc := '0001020304050607'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); /* Omit: ass_cmd.pdu.bssmap.assignmentRequest.codecList */ f_establish_fully(ass_cmd, exp_fail); } testcase TC_assignment_fr_a5_1_codec_missing() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5_1_codec_missing)); vc_conn.done; f_shutdown_helper(); } private function f_tc_assignment_fr_a5_4(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); const OCT8 kc := '0001020304050607'O; const OCT16 kc128 := kc & kc; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); f_cipher_mode('10'O, kc, kc128, true); /* TODO: expect GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED cause value */ } testcase TC_assignment_fr_a5_4() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5_4)); vc_conn.done; f_shutdown_helper(); } private function f_tc_assignment_sign(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var template PDU_BSSAP exp_compl := tr_BSSMAP_AssignmentComplete(omit, omit); var PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeSIGNAL); f_statsd_reset(); f_establish_fully(ass_cmd, exp_compl); var StatsDExpects expect := { { name := "TTCN3.bts.0.chreq.total", mtype := "c", min := 1, max := 1}, { name := "TTCN3.bts.0.chreq.successful", mtype := "c", min := 1, max := 1}, { name := "TTCN3.bsc.0.assignment.attempted", mtype := "c", min := 1, max := 1}, { name := "TTCN3.bsc.0.assignment.completed", mtype := "c", min := 1, max := 1} }; f_statsd_expect(expect); } testcase TC_assignment_sign() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_sign)); vc_conn.done; f_shutdown_helper(); } /*********************************************************************** * Codec (list) testing ***********************************************************************/ /* check if the given rsl_mode is compatible with the a_elem */ private function f_match_codec(BSSMAP_FIELD_CodecElement a_elem, RSL_IE_ChannelMode rsl_mode) return boolean { select (a_elem.codecType) { case (GSM_FR) { if (match(rsl_mode, tr_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1))) { return true; } } case (GSM_HR) { if (match(rsl_mode, tr_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1))) { return true; } } case (GSM_EFR) { if (match(rsl_mode, tr_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM2))) { return true; } } case (FR_AMR) { if (match(rsl_mode, tr_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM3))) { return true; } } case (HR_AMR) { if (match(rsl_mode, tr_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM3))) { return true; } } case else { } } return false; } /* check if the given rsl_mode is compatible with the a_list */ private function f_match_codecs(BSSMAP_IE_SpeechCodecList a_list, RSL_IE_ChannelMode rsl_mode) return boolean { for (var integer i := 0; i < sizeof(a_list); i := i+1) { if (f_match_codec(a_list.codecElements[i], rsl_mode)) { return true; } } return false; } /* determine BSSMAP_IE_ChannelType from *first* element of BSSMAP_FIELD_CodecElement */ function f_BSSMAP_chtype_from_codec(BSSMAP_FIELD_CodecElement a_elem) return BSSMAP_IE_ChannelType { /* FIXME: actually look at all elements of BSSMAP_IE_SpeechCodecList */ var BSSMAP_IE_ChannelType ret := valueof(ts_BSSMAP_IE_ChannelType); select (a_elem.codecType) { case (GSM_FR) { ret.channelRateAndType := ChRate_TCHF; ret.speechId_DataIndicator := Spdi_TCHF_FR; } case (GSM_HR) { ret.channelRateAndType := ChRate_TCHH; ret.speechId_DataIndicator := Spdi_TCHH_HR; } case (GSM_EFR) { ret.channelRateAndType := ChRate_TCHF; ret.speechId_DataIndicator := Spdi_TCHF_EFR; } case (FR_AMR) { ret.channelRateAndType := ChRate_TCHF; ret.speechId_DataIndicator := Spdi_TCHF_AMR; } case (HR_AMR) { ret.channelRateAndType := ChRate_TCHH; ret.speechId_DataIndicator := Spdi_TCHH_AMR; } case else { setverdict(fail, "Unsupported codec ", a_elem); mtc.stop; } } return ret; } private function f_rsl_chmod_tmpl_from_codec(BSSMAP_FIELD_CodecElement a_elem) return template RSL_IE_Body { var template RSL_IE_Body mode_ie := { chan_mode := { len := ?, reserved := ?, dtx_d := ?, dtx_u := ?, spd_ind := RSL_SPDI_SPEECH, ch_rate_type := -, coding_alg_rate := - } } select (a_elem.codecType) { case (GSM_FR) { mode_ie.chan_mode.ch_rate_type := RSL_CHRT_TCH_F; mode_ie.chan_mode.coding_alg_rate := RSL_CMOD_SP_GSM1; } case (GSM_HR) { mode_ie.chan_mode.ch_rate_type := RSL_CHRT_TCH_H; mode_ie.chan_mode.coding_alg_rate := RSL_CMOD_SP_GSM1; } case (GSM_EFR) { mode_ie.chan_mode.ch_rate_type := RSL_CHRT_TCH_F; mode_ie.chan_mode.coding_alg_rate := RSL_CMOD_SP_GSM2; } case (FR_AMR) { mode_ie.chan_mode.ch_rate_type := RSL_CHRT_TCH_F; mode_ie.chan_mode.coding_alg_rate := RSL_CMOD_SP_GSM3; } case (HR_AMR) { mode_ie.chan_mode.ch_rate_type := RSL_CHRT_TCH_H; mode_ie.chan_mode.coding_alg_rate := RSL_CMOD_SP_GSM3; } } return mode_ie; } type record CodecListTest { BSSMAP_IE_SpeechCodecList codec_list, charstring id } type record of CodecListTest CodecListTests private function f_TC_assignment_codec(charstring id) runs on MSC_ConnHdlr { var PDU_BSSAP ass_cmd := f_gen_ass_req(g_pars.use_osmux); var template PDU_BSSAP exp_compl := f_gen_exp_compl(g_pars.use_osmux); /* puzzle together the ASSIGNMENT REQ for given codec[s] */ if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { ass_cmd.pdu.bssmap.assignmentRequest.codecList := g_pars.ass_codec_list; exp_compl.pdu.bssmap.assignmentComplete.speechCodec.codecElements[0] := g_pars.ass_codec_list.codecElements[0]; if (isvalue(g_pars.expect_mr_s0_s7)) { exp_compl.pdu.bssmap.assignmentComplete.speechCodec.codecElements[0].s0_7 := g_pars.expect_mr_s0_s7; } } ass_cmd.pdu.bssmap.assignmentRequest.channelType := f_BSSMAP_chtype_from_codec(g_pars.ass_codec_list.codecElements[0]); log("expecting ASS COMPL like this: ", exp_compl); f_establish_fully(ass_cmd, exp_compl); /* Verify that the RSL-side activation actually matches our expectations */ var RSL_Message rsl := f_rslem_get_last_act(RSL_PROC, 0, g_chan_nr); var RSL_IE_Body mode_ie; if (f_rsl_find_ie(rsl, RSL_IE_CHAN_MODE, mode_ie) == false) { setverdict(fail, "Couldn't find CHAN_MODE IE"); mtc.stop; } var template RSL_IE_Body t_mode_ie := f_rsl_chmod_tmpl_from_codec(g_pars.ass_codec_list.codecElements[0]); if (not match(mode_ie, t_mode_ie)) { setverdict(fail, "RSL Channel Mode IE doesn't match expectation"); } var RSL_IE_Body mr_conf; if (g_pars.expect_mr_conf_ie != omit) { if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == false) { setverdict(fail, "Missing MR CONFIG IE in RSL Chan Activ"); mtc.stop; } log("found RSL MR CONFIG IE: ", mr_conf); if (not match(mr_conf, g_pars.expect_mr_conf_ie)) { setverdict(fail, "RSL MR CONFIG IE does not match expectation. Expected: ", g_pars.expect_mr_conf_ie); } } else { if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == true) { log("found RSL MR CONFIG IE: ", mr_conf); setverdict(fail, "Found MR CONFIG IE in RSL Chan Activ, expecting omit"); mtc.stop; } } } private function f_TC_assignment_codec_fail(charstring id) runs on MSC_ConnHdlr { var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; /* puzzle together the ASSIGNMENT REQ for given codec[s] */ if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { ass_cmd.pdu.bssmap.assignmentRequest.codecList := g_pars.ass_codec_list; } ass_cmd.pdu.bssmap.assignmentRequest.channelType := f_BSSMAP_chtype_from_codec(g_pars.ass_codec_list.codecElements[0]); log("expecting ASS FAIL like this: ", exp_fail); f_establish_fully(ass_cmd, exp_fail); } testcase TC_assignment_codec_fr() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_codec_hr() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR})); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_codec_efr() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecEFR})); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } /* Allow 5,90k only (current default config) */ private function f_allow_amr_rate_5_90k() runs on test_CT { f_vty_enter_cfg_msc(BSCVTY, 0); f_vty_transceive(BSCVTY, "amr-config 12_2k forbidden"); f_vty_transceive(BSCVTY, "amr-config 10_2k forbidden"); f_vty_transceive(BSCVTY, "amr-config 7_95k forbidden"); f_vty_transceive(BSCVTY, "amr-config 7_40k forbidden"); f_vty_transceive(BSCVTY, "amr-config 6_70k forbidden"); f_vty_transceive(BSCVTY, "amr-config 5_90k allowed"); f_vty_transceive(BSCVTY, "amr-config 5_15k forbidden"); f_vty_transceive(BSCVTY, "amr-config 4_75k forbidden"); f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); } /* Allow 4,75k, 5,90k, 4,70k and 12,2k, which are the most common rates * ("Config-NB-Code = 1") */ private function f_allow_amr_rate_4_75k_5_90k_7_40k_12_20k() runs on test_CT { f_vty_enter_cfg_msc(BSCVTY, 0); f_vty_transceive(BSCVTY, "amr-config 12_2k allowed"); f_vty_transceive(BSCVTY, "amr-config 10_2k forbidden"); f_vty_transceive(BSCVTY, "amr-config 7_95k forbidden"); f_vty_transceive(BSCVTY, "amr-config 7_40k allowed"); f_vty_transceive(BSCVTY, "amr-config 6_70k forbidden"); f_vty_transceive(BSCVTY, "amr-config 5_90k allowed"); f_vty_transceive(BSCVTY, "amr-config 5_15k forbidden"); f_vty_transceive(BSCVTY, "amr-config 4_75k allowed"); f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); } testcase TC_assignment_codec_amr_f() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; /* Note: This setups the codec configuration. The parameter payload in * mr_conf must be consistant with the parameter codecElements in pars * and also must match the amr-config in osmo-bsc.cfg! */ var RSL_IE_Body mr_conf := { other := { len := 2, payload := '2804'O } }; pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_F})); pars.ass_codec_list.codecElements[0].s0_7 := '00000100'B; /* 5,90k */ pars.ass_codec_list.codecElements[0].s8_15 := '01010111'B; pars.expect_mr_conf_ie := mr_conf; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_codec_amr_h() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; /* See note above */ var RSL_IE_Body mr_conf := { other := { len := 2, payload := '2804'O } }; pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H})); pars.ass_codec_list.codecElements[0].s0_7 := '00000100'B; /* 5,90k */ pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B; pars.expect_mr_conf_ie := mr_conf; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } function f_TC_assignment_codec_amr(boolean fr, octetstring mrconf, bitstring s8_s0, bitstring exp_s8_s0) runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; /* See note above */ var RSL_IE_Body mr_conf := { other := { len := lengthof(mrconf), payload := mrconf } }; if (fr) { pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_F})); } else { pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H})); } pars.ass_codec_list.codecElements[0].s0_7 := s8_s0; pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B; pars.expect_mr_conf_ie := mr_conf; pars.expect_mr_s0_s7 := exp_s8_s0; f_init(1, true); f_allow_amr_rate_4_75k_5_90k_7_40k_12_20k(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_allow_amr_rate_5_90k(); } function f_TC_assignment_codec_amr_fail(boolean fr, bitstring s8_s0) runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; if (fr) { pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_F})); } else { pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H})); } pars.ass_codec_list.codecElements[0].s0_7 := s8_s0; pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B; f_init(1, true); f_allow_amr_rate_4_75k_5_90k_7_40k_12_20k(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_assignment_codec_fail), pars); vc_conn.done; f_allow_amr_rate_5_90k(); } /* Set S1, we expect an AMR multirate configuration IE with all four rates * set. */ testcase TC_assignment_codec_amr_f_S1() runs on test_CT { f_TC_assignment_codec_amr(true, '289520882208'O, '00000010'B, '00000010'B); f_shutdown_helper(); } /* Set S1, we expect an AMR multirate configuration IE with the lower three * rates set. */ testcase TC_assignment_codec_amr_h_S1() runs on test_CT { f_TC_assignment_codec_amr(false, '2815208820'O, '00000010'B, '00000010'B); f_shutdown_helper(); } /* Set S1 and two other rates, we expect an AMR MULTIRATE CONFIGURATION IE with * all four rates (and only S1 set in the ASSIGNMENT COMPLETE) */ testcase TC_assignment_codec_amr_f_S124() runs on test_CT { f_TC_assignment_codec_amr(true, '289520882208'O, '00010110'B, '00000010'B); f_shutdown_helper(); } /* Set S1 and two other rates, we expect an AMR MULTIRATE CONFIGURATION IE with * all four rates (and only S1 set in the ASSIGNMENT COMPLETE) */ testcase TC_assignment_codec_amr_h_S124() runs on test_CT { f_TC_assignment_codec_amr(false, '2815208820'O, '00010110'B, '00000010'B); f_shutdown_helper(); } /* The following block of tests selects more and more rates until all four * possible rates are in the active set (full rate) */ testcase TC_assignment_codec_amr_f_S0() runs on test_CT { f_TC_assignment_codec_amr(true, '2801'O, '00000001'B, '00000001'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_S02() runs on test_CT { f_TC_assignment_codec_amr(true, '28052080'O, '00000101'B, '00000101'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_S024() runs on test_CT { f_TC_assignment_codec_amr(true, '2815208820'O, '00010101'B, '00010101'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_S0247() runs on test_CT { f_TC_assignment_codec_amr(true, '289520882208'O, '10010101'B, '10010101'B); f_shutdown_helper(); } /* The following block of tests selects more and more rates until all three * possible rates are in the active set (half rate) */ testcase TC_assignment_codec_amr_h_S0() runs on test_CT { f_TC_assignment_codec_amr(false, '2801'O, '00000001'B, '00000001'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_h_S02() runs on test_CT { f_TC_assignment_codec_amr(false, '28052080'O, '00000101'B, '00000101'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_h_S024() runs on test_CT { f_TC_assignment_codec_amr(false, '2815208820'O, '00010101'B, '00010101'B); f_shutdown_helper(); } /* The following block tests what happens when the MSC does offer rate * configurations that are not supported by the BSC. Normally such situations * should not happen because the MSC gets informed by the BSC in advance via * the L3 COMPLETE message which rates are applicable. The MSC should not try * to offer rates that are not applicable anyway. */ testcase TC_assignment_codec_amr_h_S0247() runs on test_CT { /* Try to include 12,2k in into the active set even though the channel * is half rate only. The BSC is expected to remove the 12,0k */ f_TC_assignment_codec_amr(false, '2815208820'O, '10010101'B, '00010101'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_S01234567() runs on test_CT { /* See what happens when all rates are selected at once. Since then * Also S1 is selected, this setting will be prefered and we should * get 12.2k, 7,40k, 5,90k, and 4,75k in the active set. */ f_TC_assignment_codec_amr(true, '289520882208'O, '11111111'B, '00000010'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_S0234567() runs on test_CT { /* Same as above, but with S1 missing, the MSC is then expected to * select the currently supported rates, which are also 12.2k, 7,40k, * 5,90k, and 4,75k, into the active set. */ f_TC_assignment_codec_amr(true, '289520882208'O, '11111101'B, '10010101'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_zero() runs on test_CT { /* Try to select no rates at all */ f_TC_assignment_codec_amr_fail(true, '00000000'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_f_unsupp() runs on test_CT { /* Try to select only unsupported rates */ f_TC_assignment_codec_amr_fail(true, '01101000'B); f_shutdown_helper(); } testcase TC_assignment_codec_amr_h_S7() runs on test_CT { /* Try to select 12,2k for half rate */ f_TC_assignment_codec_amr_fail(false, '10000000'B); f_shutdown_helper(); } private function f_disable_all_tch_f() runs on test_CT { f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 1 sub-slot 0 borken"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 2 sub-slot 0 borken"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 3 sub-slot 0 borken"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 4 sub-slot 0 borken"); } private function f_disable_all_tch_h() runs on test_CT { f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 5 sub-slot 0 borken"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 5 sub-slot 1 borken"); } private function f_enable_all_tch() runs on test_CT { f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 1 sub-slot 0 unused"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 2 sub-slot 0 unused"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 3 sub-slot 0 unused"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 4 sub-slot 0 unused"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 5 sub-slot 0 unused"); f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 5 sub-slot 1 unused"); } /* Allow HR only */ private function f_TC_assignment_codec_xr_exhausted_req_hr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '09'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '05'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR})); f_establish_fully(ass_cmd, exp_compl); } /* Allow FR only */ private function f_TC_assignment_codec_xr_exhausted_req_fr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '08'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '01'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); } /* Allow HR only (expect assignment failure) */ private function f_TC_assignment_codec_xr_exhausted_req_hr_fail(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '09'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '05'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR})); f_establish_fully(ass_cmd, exp_fail); } /* Allow FR only (expect assignment failure) */ private function f_TC_assignment_codec_xr_exhausted_req_fr_fail(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '08'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '01'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_fail); } /* Allow FR and HR, but prefer FR */ private function f_TC_assignment_codec_fr_exhausted_req_fr_hr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0A'O; /* Prefer FR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8105'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR, ts_CodecHR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000101'B; /* Expect HR */ f_establish_fully(ass_cmd, exp_compl); } /* Allow FR and HR, but prefer HR */ private function f_TC_assignment_codec_fr_exhausted_req_hr_fr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0B'O; /* Prefer HR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8501'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR, ts_CodecFR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000101'B; /* Expect HR */ f_establish_fully(ass_cmd, exp_compl); } /* Allow FR and HR, but prefer FR */ private function f_TC_assignment_codec_hr_exhausted_req_fr_hr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0A'O; /* Prefer FR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8105'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR, ts_CodecHR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000001'B; /* Expect FR */ f_establish_fully(ass_cmd, exp_compl); } /* Allow FR and HR, but prefer HR */ private function f_TC_assignment_codec_hr_exhausted_req_hr_fr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0B'O; /* Prefer HR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8501'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR, ts_CodecFR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000001'B; /* Expect FR */ f_establish_fully(ass_cmd, exp_compl); } /* Request a HR channel while all FR channels are exhausted, this is expected * to work without conflicts */ testcase TC_assignment_codec_fr_exhausted_req_hr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_f(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_xr_exhausted_req_hr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a FR channel while all FR channels are exhausted, this is expected * to fail. */ testcase TC_assignment_codec_fr_exhausted_req_fr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_f(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_xr_exhausted_req_fr_fail)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a FR (prefered) or alternatively a HR channel while all FR channels * are exhausted, this is expected to be resolved by selecting a HR channel. */ testcase TC_assignment_codec_fr_exhausted_req_fr_hr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_f(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_fr_exhausted_req_fr_hr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a HR (prefered) or alternatively a FR channel while all FR channels * are exhausted, this is expected to work without conflicts. */ testcase TC_assignment_codec_fr_exhausted_req_hr_fr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_f(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_fr_exhausted_req_hr_fr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a FR channel while all HR channels are exhausted, this is expected * to work without conflicts */ testcase TC_assignment_codec_hr_exhausted_req_fr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_h(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_xr_exhausted_req_fr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a HR channel while all HR channels are exhausted, this is expected * to fail. */ testcase TC_assignment_codec_hr_exhausted_req_hr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_h(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_xr_exhausted_req_hr_fail)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a HR (prefered) or alternatively a FR channel while all HR channels * are exhausted, this is expected to be resolved by selecting a FR channel. */ testcase TC_assignment_codec_hr_exhausted_req_hr_fr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_h(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_hr_exhausted_req_hr_fr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Request a FR (prefered) or alternatively a HR channel while all HR channels * are exhausted, this is expected to work without conflicts. */ testcase TC_assignment_codec_hr_exhausted_req_fr_hr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); f_disable_all_tch_h(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_hr_exhausted_req_fr_hr)); vc_conn.done; f_enable_all_tch(); f_shutdown_helper(); } /* Allow FR and HR, but prefer HR */ private function f_TC_assignment_codec_req_hr_fr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0B'O; /* Prefer HR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8501'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR, ts_CodecFR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000101'B; /* Expect HR */ f_establish_fully(ass_cmd, exp_compl); } /* Allow FR and HR, but prefer FR */ private function f_TC_assignment_codec_req_fr_hr(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.channelType.channelRateAndType := '0A'O; /* Prefer FR */ ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := '8105'O; ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR, ts_CodecHR})); exp_compl.pdu.bssmap.assignmentComplete.speechVersion.speechVersionIdentifier := '0000001'B; /* Expect FR */ f_establish_fully(ass_cmd, exp_compl); } /* Request a HR (prefered) or alternatively a FR channel, it is expected that * HR, which is the prefered type, is selected. */ testcase TC_assignment_codec_req_hr_fr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_req_hr_fr)); vc_conn.done; f_shutdown_helper(); } /* Request a FR (prefered) or alternatively a HR channel, it is expected that * FR, which is the prefered type, is selected. */ testcase TC_assignment_codec_req_fr_hr() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_enable_all_tch(); vc_conn := f_start_handler(refers(f_TC_assignment_codec_req_fr_hr)); vc_conn.done; f_shutdown_helper(); } testcase TC_assignment_osmux() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; /* See note above */ var RSL_IE_Body mr_conf := { other := { len := 2, payload := '2804'O } }; pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H})); pars.ass_codec_list.codecElements[0].s0_7 := '00000100'B; /* 5,90k */ pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B; pars.expect_mr_conf_ie := mr_conf; pars.use_osmux := true; f_init(1, true, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars); vc_conn.done; f_shutdown_helper(); } /* test the procedure of the MSC requesting a Classmark Update: * a) BSSMAP Classmark Request should result in RR CLASSMARK ENQUIRY, * b) L3 RR CLASSMARK CHANGE should result in BSSMAP CLASSMARK UPDATE */ private function f_tc_classmark(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); f_create_chan_and_exp(); /* we should now have a COMPL_L3 at the MSC */ BSSAP.receive(tr_BSSMAP_ComplL3); BSSAP.send(ts_BSSMAP_ClassmarkRequest); RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_CM_ENQUIRY)); f_rsl_send_l3(ts_RRM_CM_CHG(valueof(ts_CM2))); BSSAP.receive(tr_BSSMAP_ClassmarkUpd(?, omit)); setverdict(pass); } testcase TC_classmark() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_classmark)); vc_conn.done; f_shutdown_helper(); } /* Send a CommonID from the simulated MSC and verify that the information is used to * fill BSC-internal data structures (specifically, bsc_subscr associated with subscr_conn) */ private function f_tc_common_id(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); f_MscConnHdlr_init_vty(); f_create_chan_and_exp(); /* we should now have a COMPL_L3 at the MSC */ BSSAP.receive(tr_BSSMAP_ComplL3); /* Send CommonID */ BSSAP.send(ts_BSSMAP_CommonId(g_pars.imsi)); /* Use VTY to verify that the IMSI of the subscr_conn is set */ var charstring regex := "*(IMSI: " & hex2str(g_pars.imsi) & ")*"; f_vty_transceive_match_regexp_retry(BSCVTY, "show conns", regex, 0, 4, 1.0); setverdict(pass); } testcase TC_common_id() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_common_id)); vc_conn.done; f_shutdown_helper(); } private function f_est_single_l3(template PDU_ML3_MS_NW l3) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); f_create_chan_and_exp(); /* we should now have a COMPL_L3 at the MSC */ BSSAP.receive(tr_BSSMAP_ComplL3); /* send the single message we want to send */ f_rsl_send_l3(l3); } private function f_bssap_expect_nothing(float sec := 5.00) runs on MSC_ConnHdlr { timer T := sec; var PDU_BSSAP bssap; T.start; alt { [] BSSAP.receive(PDU_BSSAP:?) -> value bssap { setverdict(fail, "Unexpected BSSMAP ", bssap); mtc.stop; } [] T.timeout { setverdict(pass); } } } /* unsolicited ASSIGNMENT FAIL (without ASSIGN) from MS shouldn't bring BSC down */ private function f_tc_unsol_ass_fail(charstring id) runs on MSC_ConnHdlr { f_est_single_l3(ts_RRM_AssignmentFailure('00'O)); f_bssap_expect_nothing(); } testcase TC_unsol_ass_fail() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_unsol_ass_fail)); vc_conn.done; f_shutdown_helper(); } /* unsolicited ASSIGNMENT COMPLETE (without ASSIGN) from MS shouldn't bring BSC down */ private function f_tc_unsol_ass_compl(charstring id) runs on MSC_ConnHdlr { f_est_single_l3(ts_RRM_AssignmentComplete('00'O)); f_bssap_expect_nothing(); } testcase TC_unsol_ass_compl() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_unsol_ass_compl)); vc_conn.done; f_shutdown_helper(); } /* unsolicited HANDOVER FAIL (without ASSIGN) from MS shouldn't bring BSC down */ private function f_tc_unsol_ho_fail(charstring id) runs on MSC_ConnHdlr { f_est_single_l3(ts_RRM_HandoverFailure('00'O)); f_bssap_expect_nothing(); } testcase TC_unsol_ho_fail() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_unsol_ho_fail)); vc_conn.done; f_shutdown_helper(); } /* short message from MS should be ignored */ private function f_tc_err_82_short_msg(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); f_create_chan_and_exp(); /* we should now have a COMPL_L3 at the MSC */ BSSAP.receive(tr_BSSMAP_ComplL3); /* send short message */ RSL.send(ts_RSL_DATA_IND(g_chan_nr, valueof(ts_RslLinkID_DCCH(0)), ''O)); f_bssap_expect_nothing(); } testcase TC_err_82_short_msg() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_err_82_short_msg)); vc_conn.done; f_shutdown_helper(); } /* 24.008 8.4 Unknown message must trigger RR STATUS */ private function f_tc_err_84_unknown_msg(charstring id) runs on MSC_ConnHdlr { f_est_single_l3(ts_RRM_UL_REL('00'O)); timer T := 3.0 alt { [] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_RR_STATUS)) { setverdict(pass); } [] BSSAP.receive { setverdict(fail, "unexpected BSSAP"); } [] T.timeout { setverdict(fail, "Timeout waiting for RR STATUS"); } } } testcase TC_err_84_unknown_msg() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_err_84_unknown_msg)); vc_conn.done; f_shutdown_helper(); } /*********************************************************************** * Handover ***********************************************************************/ /* execute a "bts <0-255> trx <0-255> timeslot <0-7> " command on given Dchan */ private function f_vty_ts_action(charstring suffix, integer bts_nr, integer trx_nr, integer ts_nr) runs on test_CT { var charstring cmd := "bts "&int2str(bts_nr)&" trx "&int2str(trx_nr)& " timeslot "&int2str(ts_nr)&" "; f_vty_transceive(BSCVTY, cmd & suffix); } /* execute a "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7>" command on given Dchan */ private function f_vty_ss_action(TELNETasp_PT pt, charstring suffix, uint8_t bts_nr, uint8_t trx_nr, in RslChannelNr chan_nr) { /* FIXME: resolve those from component-global state */ var integer ts_nr := chan_nr.tn; var integer ss_nr; if (ischosen(chan_nr.u.ch0)) { ss_nr := 0; } else if (ischosen(chan_nr.u.lm)) { ss_nr := chan_nr.u.lm.sub_chan; } else if (ischosen(chan_nr.u.sdcch4)) { ss_nr := chan_nr.u.sdcch4.sub_chan; } else if (ischosen(chan_nr.u.sdcch8)) { ss_nr := chan_nr.u.sdcch8.sub_chan; } else { setverdict(fail, "Invalid ChanNr ", chan_nr); mtc.stop; } var charstring cmd := "bts "&int2str(bts_nr)&" trx "&int2str(trx_nr)& " timeslot "&int2str(ts_nr)&" sub-slot "&int2str(ss_nr)&" "; f_vty_transceive(pt, cmd & suffix); } /* Even though the VTY command to trigger handover takes a new BTS number as argument, behind the scenes osmo-bsc always * translates that to a target ARFCN+BSIC first. See bsc_vty.c trigger_ho_or_as(), which puts the selected BTS' neighbor * ident key (ARFCN + BSIC) in the struct passed on to handover_request(). handover_start() then resolves that to a * viable actual neighbor cell. So from the internal osmo-bsc perspective, we always request handover to an ARFCN + BSIC * pair, not really to a specific BTS number. */ private function f_vty_handover(TELNETasp_PT pt, uint8_t bts_nr, uint8_t trx_nr, in RslChannelNr chan_nr, uint8_t new_bts_nr) { f_vty_ss_action(pt, "handover " & int2str(new_bts_nr), bts_nr, trx_nr, chan_nr); } /* intra-BSC hand-over between BTS0 and BTS1 */ private function f_tc_ho_int(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); const OCT8 kc := '0001020304050607'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); f_establish_fully(ass_cmd, exp_compl); f_bts_0_cfg(BSCVTY, {"neighbor bts 1"}); var HandoverState hs := { rr_ho_cmpl_seen := false, handover_done := false, old_chan_nr := - }; /* issue hand-over command on VTY */ f_vty_handover(BSCVTY, 0, 0, g_chan_nr, 1); /* temporarily suspend DChan processing on BTS1 to avoid race with RSLEM_register */ f_rslem_suspend(RSL1_PROC); /* From the MGW perspective, a handover is is characterized by * performing one MDCX operation with the MGW. So we expect to see * one more MDCX during handover. */ g_media.mgcp_conn[0].mdcx_seen_exp := g_media.mgcp_conn[0].crcx_seen_exp + 1; alt { [] as_handover(hs); } /* Since this is an internal handover we expect the BSC to inform the * MSC about the event */ BSSAP.receive(tr_BSSMAP_HandoverPerformed); /* Check the amount of MGCP transactions is still consistant with the * test expectation */ f_check_mgcp_expectations() f_sleep(0.5); } testcase TC_ho_int() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(2, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_int)); vc_conn.done; /* from f_establish_fully() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* from handover */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:completed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:completed"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* Expecting MGCP to DLCX the endpoint's two connections: towards BTS and towards MSC */ private function f_expect_dlcx_conns() runs on MSC_ConnHdlr { var MgcpCommand mgcp; var template MgcpResponse mgcp_resp; var MGCP_RecvFrom mrf; var template MgcpMessage msg_resp; var template MgcpMessage msg_dlcx := { command := tr_DLCX() } if (g_pars.aoip) { MGCP.receive(tr_DLCX()) -> value mgcp { log("Got first DLCX: ", mgcp); MGCP.send(ts_DLCX_ACK2(mgcp.line.trans_id)); }; MGCP.receive(tr_DLCX()) -> value mgcp { log("Got second DLCX: ", mgcp); MGCP.send(ts_DLCX_ACK2(mgcp.line.trans_id)); }; } else { /* For SCCPLite, BSC doesn't handle the MSC-side */ MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_dlcx)) -> value mrf { log("Got first DLCX: ", mrf.msg.command); msg_resp := { response := ts_DLCX_ACK2(mrf.msg.command.line.trans_id) } MGCP_MULTI.send(t_MGCP_SendToMrf(mrf, msg_resp)); }; } BSSAP.receive(tr_BSSMAP_ClearComplete); } private function f_tc_ho_out_of_this_bsc(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_req := f_gen_ass_req(); ass_req.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_req.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); f_establish_fully(ass_req, exp_compl); f_bts_0_cfg(BSCVTY, {"neighbor lac 99 arfcn 123 bsic any"}); f_vty_transceive(BSCVTY, "handover any to arfcn 123 bsic any"); BSSAP.receive(tr_BSSMAP_HandoverRequired); f_sleep(0.5); /* The MSC negotiates Handover Request and Handover Request Ack with * the other BSS and comes back with a BSSMAP Handover Command * containing an RR Handover Command coming from the target BSS... */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); log("Remote cell's RR Handover Command passed through as L3 Info: ", rr_ho_cmd); var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); log("Remote cell's RR Handover Command passed through as L3 Info, encoded: ", rr_ho_cmd_enc); BSSAP.send(ts_BSSMAP_HandoverCommand(rr_ho_cmd_enc)); /* expect the Handover Command to go out on RR */ var RSL_Message rsl_ho_cmd RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, ?)) -> value rsl_ho_cmd; log("RSL Data Req went out to first BTS: ", rsl_ho_cmd); var RSL_IE_Body rsl_ho_cmd_l3; if (not f_rsl_find_ie(rsl_ho_cmd, RSL_IE_L3_INFO, rsl_ho_cmd_l3)) { log("RSL message contains no L3 Info IE, expected RR Handover Command"); setverdict(fail); } else { log("Found L3 Info: ", rsl_ho_cmd_l3); if (rsl_ho_cmd_l3.l3_info.payload != rr_ho_cmd_enc) { log("FAIL: the BSC sent out a different L3 Info, not matching the RR Handover Command the other BSS forwarded."); setverdict(fail); } else { log("Success: the BSC sent out the same RR Handover Command the other BSS forwarded."); setverdict(pass); } } /* When the other BSS has reported a completed handover, this side is * torn down. */ var myBSSMAP_Cause cause_val := GSM0808_CAUSE_HANDOVER_SUCCESSFUL; var BssmapCause cause := enum2int(cause_val); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); f_expect_dlcx_conns(); setverdict(pass); f_sleep(1.0); } testcase TC_ho_out_of_this_bsc() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_out_of_this_bsc)); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:completed"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:completed"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_mo_l3_transceive(RSL_DCHAN_PT rsl := RSL, template (value) RslLinkId link_id := ts_RslLinkID_DCCH(0), template (present) OCT1 dlci := ?, octetstring l3 := '0123456789'O) runs on MSC_ConnHdlr { /* The old lchan and conn should still be active. See that arbitrary L3 * is still going through. */ rsl.send(ts_RSL_DATA_IND(g_chan_nr, link_id, l3)); var template PDU_BSSAP exp_data := { discriminator := '1'B, spare := '0000000'B, dlci := dlci, lengthIndicator := lengthof(l3), pdu := { dtap := l3 } }; BSSAP.receive(exp_data); setverdict(pass); } private function f_mt_l3_transceive(RSL_DCHAN_PT rsl := RSL, template (present) RslLinkId link_id := tr_RslLinkID_DCCH(0), template (value) OCT1 dlci := '00'O, octetstring l3 := '0123456789'O) runs on MSC_ConnHdlr { BSSAP.send(PDU_BSSAP:{ discriminator := '1'B, spare := '0000000'B, dlci := dlci, lengthIndicator := lengthof(l3), pdu := { dtap := l3 } }); rsl.receive(tr_RSL_DATA_REQ(g_chan_nr, link_id, l3)); setverdict(pass); } /* BSC asks for inter-BSC HO, but the MSC decides that it won't happen and * simply never sends a BSSMAP Handover Command. */ private function f_tc_ho_out_fail_no_msc_response(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_req := f_gen_ass_req(); ass_req.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_req.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); f_establish_fully(ass_req, exp_compl); f_bts_0_cfg(BSCVTY, {"neighbor lac 99 arfcn 123 bsic any"}); f_vty_transceive(BSCVTY, "handover any to arfcn 123 bsic any"); BSSAP.receive(tr_BSSMAP_HandoverRequired); /* osmo-bsc should time out 10 seconds after the handover started. * Let's give it a bit extra. */ f_sleep(15.0); f_mo_l3_transceive(); f_sleep(1.0); } testcase TC_ho_out_fail_no_msc_response() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_out_fail_no_msc_response)); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:timeout"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:timeout"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* BSC asks for inter-BSC HO, receives BSSMAP Handover Command, but MS reports * RR Handover Failure. */ private function f_tc_ho_out_fail_rr_ho_failure(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_req := f_gen_ass_req(); ass_req.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_req.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); f_establish_fully(ass_req, exp_compl); f_bts_0_cfg(BSCVTY, {"neighbor lac 99 arfcn 123 bsic any"}); f_vty_transceive(BSCVTY, "handover any to arfcn 123 bsic any"); BSSAP.receive(tr_BSSMAP_HandoverRequired); f_sleep(0.5); /* The MSC negotiates Handover Request and Handover Request Ack with * the other BSS and comes back with a BSSMAP Handover Command * containing an RR Handover Command coming from the target BSS... */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); log("Remote cell's RR Handover Command passed through as L3 Info: ", rr_ho_cmd); var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); log("Remote cell's RR Handover Command passed through as L3 Info, encoded: ", rr_ho_cmd_enc); BSSAP.send(ts_BSSMAP_HandoverCommand(rr_ho_cmd_enc)); /* expect the Handover Command to go out on RR */ var RSL_Message rsl_ho_cmd RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, ?)) -> value rsl_ho_cmd; log("RSL Data Req went out to first BTS: ", rsl_ho_cmd); var RSL_IE_Body rsl_ho_cmd_l3; if (not f_rsl_find_ie(rsl_ho_cmd, RSL_IE_L3_INFO, rsl_ho_cmd_l3)) { log("RSL message contains no L3 Info IE, expected RR Handover Command"); setverdict(fail); } else { log("Found L3 Info: ", rsl_ho_cmd_l3); if (rsl_ho_cmd_l3.l3_info.payload != rr_ho_cmd_enc) { log("FAIL: the BSC sent out a different L3 Info, not matching the RR Handover Command the other BSS forwarded."); setverdict(fail); } else { log("Success: the BSC sent out the same RR Handover Command the other BSS forwarded."); setverdict(pass); } } f_sleep(0.2); f_rsl_send_l3(ts_RRM_HandoverFailure('00'O)); /* Should tell the MSC about the failure */ BSSAP.receive(tr_BSSMAP_HandoverFailure); f_sleep(1.0); f_mo_l3_transceive(); f_sleep(1.0); setverdict(pass); f_sleep(1.0); } testcase TC_ho_out_fail_rr_ho_failure() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_out_fail_rr_ho_failure)); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:failed"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* BSC asks for inter-BSC-out HO, receives BSSMAP Handover Command, but then no reply is received about HO outcome * (neither BSSMAP Clear Command for success nor RR Handover Failure). 48.008 3.1.5.3.3 "Abnormal Conditions" applies * and the lchan is released. */ private function f_tc_ho_out_fail_no_result_after_ho_cmd(charstring id) runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var PDU_BSSAP ass_req := f_gen_ass_req(); ass_req.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_req.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); f_establish_fully(ass_req, exp_compl); f_bts_0_cfg(BSCVTY, {"neighbor lac 99 arfcn 123 bsic any"}); f_vty_transceive(BSCVTY, "handover any to arfcn 123 bsic any"); BSSAP.receive(tr_BSSMAP_HandoverRequired); f_sleep(0.5); /* The MSC negotiates Handover Request and Handover Request Ack with * the other BSS and comes back with a BSSMAP Handover Command * containing an RR Handover Command coming from the target BSS... */ var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); log("Remote cell's RR Handover Command passed through as L3 Info: ", rr_ho_cmd); var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); log("Remote cell's RR Handover Command passed through as L3 Info, encoded: ", rr_ho_cmd_enc); BSSAP.send(ts_BSSMAP_HandoverCommand(rr_ho_cmd_enc)); /* expect the Handover Command to go out on RR */ var RSL_Message rsl_ho_cmd RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, ?)) -> value rsl_ho_cmd; log("RSL Data Req went out to first BTS: ", rsl_ho_cmd); var RSL_IE_Body rsl_ho_cmd_l3; if (not f_rsl_find_ie(rsl_ho_cmd, RSL_IE_L3_INFO, rsl_ho_cmd_l3)) { log("RSL message contains no L3 Info IE, expected RR Handover Command"); setverdict(fail); } else { log("Found L3 Info: ", rsl_ho_cmd_l3); if (rsl_ho_cmd_l3.l3_info.payload != rr_ho_cmd_enc) { log("FAIL: the BSC sent out a different L3 Info, not matching the RR Handover Command the other BSS forwarded."); setverdict(fail); } else { log("Success: the BSC sent out the same RR Handover Command the other BSS forwarded."); setverdict(pass); } } /* We get neither success nor failure report from the remote BSS. Eventually T8 times out and we run into 3GPP * TS 48.008 3.1.5.3.3 "Abnormal Conditions": Clear Request should go to the MSC, and RR should be released * after Clear Command */ var PDU_BSSAP rx_clear_request; BSSAP.receive(tr_BSSMAP_ClearRequest) -> value rx_clear_request; log("Got BSSMAP Clear Request"); /* Instruct BSC to clear channel */ var BssmapCause cause := bit2int(rx_clear_request.pdu.bssmap.clearRequest.cause.causeValue); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); var MgcpCommand mgcp; interleave { [] RSL.receive(tr_RSL_DEACT_SACCH(g_chan_nr)) { log("Got Deact SACCH"); } [] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_RR_RELEASE)) { log("Got RR Release"); } [] RSL.receive(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL)) { log("Got RF Chan Rel"); RSL.send(ts_RSL_RF_CHAN_REL_ACK(g_chan_nr)); } } f_expect_dlcx_conns(); setverdict(pass); f_sleep(1.0); } testcase TC_ho_out_fail_no_result_after_ho_cmd() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_out_fail_no_result_after_ho_cmd)); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:timeout"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:timeout"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_into_this_bsc(charstring id) runs on MSC_ConnHdlr { /* Hack: the proper way would be to wait for the BSSMAP Handover Request ACK and extract the * actual assigned chan_nr from its L3 (RR Handover Command) message. But osmo-bsc starts acting * on the lchan even before we get a chance to evaluate the BSSMAP Handover Request ACK. So we * need to assume that osmo-bsc will activate TS 1 and already set up this lchan's RSL emulation * before we get started. */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); f_rslem_register(0, new_chan_nr); g_chan_nr := new_chan_nr; f_sleep(1.0); f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); activate(as_Media()); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, f_gen_handover_req(aoip_tla := g_pars.host_aoip_tla))); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* The RSL Emulation magically accepts the Chan Activ behind the scenes. */ var PDU_BSSAP rx_bssap; var octetstring ho_command_str; BSSAP.receive(tr_BSSMAP_HandoverRequestAcknowledge(?)) -> value rx_bssap; ho_command_str := rx_bssap.pdu.bssmap.handoverRequestAck.layer3Information.layer3info; log("Received L3 Info in HO Request Ack: ", ho_command_str); var PDU_ML3_NW_MS ho_command := dec_PDU_ML3_NW_MS(ho_command_str); log("L3 Info in HO Request Ack is ", ho_command); var GsmArfcn arfcn; var RslChannelNr actual_new_chan_nr; f_ChDesc2RslChanNr(ho_command.msgs.rrm.handoverCommand.channelDescription2, actual_new_chan_nr, arfcn); if (actual_new_chan_nr != new_chan_nr) { log("ERROR: osmo-bsc assigned a different lchan than we assumed above -- this test will fail now.", " Assumed: ", new_chan_nr, " Assigned: ", actual_new_chan_nr); setverdict(fail); return; } log("Handover Command chan_nr is", actual_new_chan_nr); /* Now the MSC forwards the RR Handover Command to the other BSC, which * tells the MS to handover to the new lchan. Here comes the new MS on * the new lchan with a Handover RACH: */ /* send handover detect */ RSL.send(ts_RSL_HANDO_DET(new_chan_nr)); BSSAP.receive(tr_BSSMAP_HandoverDetect); /* send handover complete over the new channel */ var PDU_ML3_MS_NW l3_tx := valueof(ts_RRM_HandoverComplete('00'O)); RSL.send(ts_RSL_EST_IND(new_chan_nr, valueof(ts_RslLinkID_DCCH(0)), enc_PDU_ML3_MS_NW(l3_tx))); BSSAP.receive(tr_BSSMAP_HandoverComplete); setverdict(pass); } function f_tc_ho_into_this_bsc_main(TestHdlrParams pars) runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_ho_into_this_bsc), pars); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:completed"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:completed"); f_ctrs_bsc_and_bts_verify(); } testcase TC_ho_into_this_bsc() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_tc_ho_into_this_bsc_main(pars); f_shutdown_helper(); } testcase TC_ho_into_this_bsc_tla_v6() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); pars.host_aoip_tla := "::6"; f_tc_ho_into_this_bsc_main(pars); f_shutdown_helper(); } private function f_tc_ho_in_fail_msc_clears(charstring id) runs on MSC_ConnHdlr { var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); f_rslem_register(0, new_chan_nr); g_chan_nr := new_chan_nr; f_sleep(1.0); f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); activate(as_Media()); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, f_gen_handover_req())); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* The RSL Emulation magically accepts the Chan Activ behind the scenes. */ var PDU_BSSAP rx_bssap; var octetstring ho_command_str; BSSAP.receive(tr_BSSMAP_HandoverRequestAcknowledge(?)) -> value rx_bssap; ho_command_str := rx_bssap.pdu.bssmap.handoverRequestAck.layer3Information.layer3info; log("Received L3 Info in HO Request Ack: ", ho_command_str); var PDU_ML3_NW_MS ho_command := dec_PDU_ML3_NW_MS(ho_command_str); log("L3 Info in HO Request Ack is ", ho_command); var GsmArfcn arfcn; var RslChannelNr actual_new_chan_nr; f_ChDesc2RslChanNr(ho_command.msgs.rrm.handoverCommand.channelDescription2, actual_new_chan_nr, arfcn); if (actual_new_chan_nr != new_chan_nr) { log("ERROR: osmo-bsc assigned a different lchan than we assumed above -- this test will fail now.", " Assumed: ", new_chan_nr, " Assigned: ", actual_new_chan_nr); setverdict(fail); return; } log("Handover Command chan_nr is", actual_new_chan_nr); /* For deterministic test results, give some time for the MGW endpoint to be configured */ f_sleep(1.0); /* Now the MSC forwards the RR Handover Command to the other BSC, which * tells the MS to handover to the new lchan. In this case, the MS * reports a Handover Failure to the old BSS, which forwards a BSSMAP * Handover Failure to the MSC. The procedure according to 3GPP TS * 48.008 3.1.5.3.2 "Handover Failure" is then that the MSC sends a * BSSMAP Clear Command: */ var myBSSMAP_Cause cause_val := GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION; var BssmapCause cause := enum2int(cause_val); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); f_expect_dlcx_conns(); setverdict(pass); f_sleep(1.0); setverdict(pass); } testcase TC_ho_in_fail_msc_clears() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_ho_in_fail_msc_clears), pars); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:stopped"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:stopped"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_in_fail_msc_clears_after_ho_detect(charstring id) runs on MSC_ConnHdlr { /* Hack: the proper way would be to wait for the BSSMAP Handover Request ACK and extract the * actual assigned chan_nr from its L3 (RR Handover Command) message. But osmo-bsc starts acting * on the lchan even before we get a chance to evaluate the BSSMAP Handover Request ACK. So we * need to assume that osmo-bsc will activate TS 1 and already set up this lchan's RSL emulation * before we get started. */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); f_rslem_register(0, new_chan_nr); g_chan_nr := new_chan_nr; f_sleep(1.0); f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); activate(as_Media()); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, f_gen_handover_req())); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* The RSL Emulation magically accepts the Chan Activ behind the scenes. */ var PDU_BSSAP rx_bssap; var octetstring ho_command_str; BSSAP.receive(tr_BSSMAP_HandoverRequestAcknowledge(?)) -> value rx_bssap; ho_command_str := rx_bssap.pdu.bssmap.handoverRequestAck.layer3Information.layer3info; log("Received L3 Info in HO Request Ack: ", ho_command_str); var PDU_ML3_NW_MS ho_command := dec_PDU_ML3_NW_MS(ho_command_str); log("L3 Info in HO Request Ack is ", ho_command); var GsmArfcn arfcn; var RslChannelNr actual_new_chan_nr; f_ChDesc2RslChanNr(ho_command.msgs.rrm.handoverCommand.channelDescription2, actual_new_chan_nr, arfcn); if (actual_new_chan_nr != new_chan_nr) { log("ERROR: osmo-bsc assigned a different lchan than we assumed above -- this test will fail now.", " Assumed: ", new_chan_nr, " Assigned: ", actual_new_chan_nr); setverdict(fail); return; } log("Handover Command chan_nr is", actual_new_chan_nr); /* Now the MSC forwards the RR Handover Command to the other BSC, which * tells the MS to handover to the new lchan. Here comes the new MS on * the new lchan with a Handover RACH: */ /* send handover detect */ RSL.send(ts_RSL_HANDO_DET(new_chan_nr)); BSSAP.receive(tr_BSSMAP_HandoverDetect); /* The MSC chooses to clear the connection now, maybe we got the * Handover RACH on the new cell but the MS still signaled Handover * Failure to the old BSS? */ var myBSSMAP_Cause cause_val := GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION; var BssmapCause cause := enum2int(cause_val); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); f_expect_dlcx_conns(); f_sleep(1.0); } testcase TC_ho_in_fail_msc_clears_after_ho_detect() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_ho_in_fail_msc_clears_after_ho_detect), pars); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:stopped"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:stopped"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* The new BSS's lchan times out before the MSC decides that handover failed. */ private function f_tc_ho_in_fail_no_detect(charstring id) runs on MSC_ConnHdlr { var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); f_rslem_register(0, new_chan_nr); g_chan_nr := new_chan_nr; f_sleep(1.0); f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); activate(as_Media()); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, f_gen_handover_req())); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* The RSL Emulation magically accepts the Chan Activ behind the scenes. */ var PDU_BSSAP rx_bssap; var octetstring ho_command_str; BSSAP.receive(tr_BSSMAP_HandoverRequestAcknowledge(?)) -> value rx_bssap; ho_command_str := rx_bssap.pdu.bssmap.handoverRequestAck.layer3Information.layer3info; log("Received L3 Info in HO Request Ack: ", ho_command_str); var PDU_ML3_NW_MS ho_command := dec_PDU_ML3_NW_MS(ho_command_str); log("L3 Info in HO Request Ack is ", ho_command); var GsmArfcn arfcn; var RslChannelNr actual_new_chan_nr; f_ChDesc2RslChanNr(ho_command.msgs.rrm.handoverCommand.channelDescription2, actual_new_chan_nr, arfcn); if (actual_new_chan_nr != new_chan_nr) { log("ERROR: osmo-bsc assigned a different lchan than we assumed above -- this test will fail now.", " Assumed: ", new_chan_nr, " Assigned: ", actual_new_chan_nr); setverdict(fail); return; } log("Handover Command chan_nr is", actual_new_chan_nr); /* Now the MSC forwards the RR Handover Command to the other BSC, which * tells the MS to handover to the new lchan. But the MS never shows up * on the new lchan. */ BSSAP.receive(tr_BSSMAP_HandoverFailure); /* Did osmo-bsc also send a Clear Request? */ timer T := 0.5; T.start; alt { [] BSSAP.receive(tr_BSSMAP_ClearRequest); [] T.timeout { } } /* MSC plays along with a Clear Command (no matter whether osmo-bsc * asked for it, this is a Handover Failure after all). */ var myBSSMAP_Cause cause_val := GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION; var BssmapCause cause := enum2int(cause_val); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); f_expect_dlcx_conns(); f_sleep(1.0); } testcase TC_ho_in_fail_no_detect() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_ho_in_fail_no_detect), pars); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:error"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* Same as f_tc_ho_in_fail_no_detect, but MSC fails to send a Clear Command */ private function f_tc_ho_in_fail_no_detect2(charstring id) runs on MSC_ConnHdlr { var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); f_rslem_register(0, new_chan_nr); g_chan_nr := new_chan_nr; f_sleep(1.0); f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit}); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); activate(as_Media()); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, f_gen_handover_req())); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* The RSL Emulation magically accepts the Chan Activ behind the scenes. */ var PDU_BSSAP rx_bssap; var octetstring ho_command_str; BSSAP.receive(tr_BSSMAP_HandoverRequestAcknowledge(?)) -> value rx_bssap; ho_command_str := rx_bssap.pdu.bssmap.handoverRequestAck.layer3Information.layer3info; log("Received L3 Info in HO Request Ack: ", ho_command_str); var PDU_ML3_NW_MS ho_command := dec_PDU_ML3_NW_MS(ho_command_str); log("L3 Info in HO Request Ack is ", ho_command); var GsmArfcn arfcn; var RslChannelNr actual_new_chan_nr; f_ChDesc2RslChanNr(ho_command.msgs.rrm.handoverCommand.channelDescription2, actual_new_chan_nr, arfcn); if (actual_new_chan_nr != new_chan_nr) { log("ERROR: osmo-bsc assigned a different lchan than we assumed above -- this test will fail now.", " Assumed: ", new_chan_nr, " Assigned: ", actual_new_chan_nr); setverdict(fail); return; } log("Handover Command chan_nr is", actual_new_chan_nr); /* Now the MSC forwards the RR Handover Command to the other BSC, which * tells the MS to handover to the new lchan. But the MS never shows up * on the new lchan. */ BSSAP.receive(tr_BSSMAP_HandoverFailure); /* MSC plays dumb and sends no Clear Command */ var PDU_BSSAP rx_clear_request; BSSAP.receive(tr_BSSMAP_ClearRequest) -> value rx_clear_request { var BssmapCause cause := bit2int(rx_clear_request.pdu.bssmap.clearRequest.cause.causeValue); BSSAP.send(ts_BSSMAP_ClearCommand(cause)); }; f_expect_dlcx_conns(); f_sleep(1.0); } testcase TC_ho_in_fail_no_detect2() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_ho_in_fail_no_detect2), pars); vc_conn.done; f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:error"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } type record of charstring Commands; private function f_bts_0_cfg(TELNETasp_PT pt, Commands cmds := {}) { f_vty_enter_cfg_bts(pt, 0); for (var integer i := 0; i < sizeof(cmds); i := i+1) { f_vty_transceive(pt, cmds[i]); } f_vty_transceive(pt, "end"); } private function f_cs7_inst_0_cfg(TELNETasp_PT pt, Commands cmds := {}) { f_vty_enter_cfg_cs7_inst(pt, 0); for (var integer i := 0; i < sizeof(cmds); i := i+1) { f_vty_transceive(pt, cmds[i]); } f_vty_transceive(pt, "end"); } private function f_probe_for_handover(charstring log_label, charstring log_descr, charstring handover_vty_cmd, boolean expect_handover, boolean is_inter_bsc_handover := false) runs on MSC_ConnHdlr { /* We're going to thwart any and all handover attempts, just be ready to handle (and ignore) handover target * lchans to be established on bts 1 or bts 2. */ f_rslem_suspend(RSL1_PROC); f_rslem_suspend(RSL2_PROC); var RSL_Message rsl; var charstring log_msg := " (expecting handover)" if (not expect_handover) { log_msg := " (expecting NO handover)"; } log("f_probe_for_handover starting: " & log_label & ": " & log_descr & log_msg); f_vty_transceive(BSCVTY, handover_vty_cmd); timer T := 2.0; T.start; alt { [] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr)) -> value rsl { var PDU_ML3_NW_MS l3 := dec_PDU_ML3_NW_MS(rsl.ies[2].body.l3_info.payload); log("Rx L3 from net: ", l3); if (ischosen(l3.msgs.rrm.handoverCommand)) { var RslChannelNr new_chan_nr; var GsmArfcn arfcn; f_ChDesc2RslChanNr(l3.msgs.rrm.handoverCommand.channelDescription2, new_chan_nr, arfcn); log("Handover to new chan ", new_chan_nr, " on ARFCN ", arfcn); log(l3.msgs.rrm.handoverCommand); /* Need to register for new lchan on new BTS -- it's either bts 1 or bts 2. It doesn't really * matter on which BTS it really is, we're not going to follow through an entire handover * anyway. */ f_rslem_register(0, new_chan_nr, RSL1_PROC); f_rslem_resume(RSL1_PROC); f_rslem_register(0, new_chan_nr, RSL2_PROC); f_rslem_resume(RSL2_PROC); if (expect_handover and not is_inter_bsc_handover) { setverdict(pass); log("f_probe_for_handover(" & log_label & "): Got RSL Handover Command as expected."); } else { setverdict(fail, "f_probe_for_handover(" & log_label & "): Expected none, but got RSL Handover Command. " & log_label & ": " & log_descr); } log("f_probe_for_handover(" & log_label & "): Ending the test: Handover Failure stops the procedure."); /* osmo-bsc has triggered Handover. That's all we need to know for this test, reply with * Handover Failure. */ f_rsl_send_l3(ts_RRM_HandoverFailure('00'O)); /* target BTS is told to release lchan again; don't care which BTS nor what messages. */ f_sleep(0.5); RSL1.clear; RSL2.clear; log("f_probe_for_handover(" & log_label & "): done (got RSL Handover Command)"); break; } else { repeat; } } [] BSSAP.receive(tr_BSSMAP_HandoverRequired) { if (expect_handover and is_inter_bsc_handover) { setverdict(pass); log("f_probe_for_handover(" & log_label & "): Got BSSMAP Handover Required as expected."); } else { setverdict(fail, "f_probe_for_handover(" & log_label & "): Expected none, but got BSSMAP Handover Required. " & log_label & ": " & log_descr); } log("f_probe_for_handover(" & log_label & "): done (got BSSMAP Handover Required)"); /* Note: f_tc_ho_neighbor_config_start() sets T7, the timeout for BSSMAP Handover Required, to * 1 second. There is no legal way to quickly abort a handover after a BSSMAP Handover Required, * setting a short timeout and waiting is the only way. */ log("f_probe_for_handover(" & log_label & "): waiting for inter-BSC HO to time out..."); f_sleep(1.5); log("f_probe_for_handover(" & log_label & "): ...done"); break; } [] T.timeout { if (expect_handover) { setverdict(fail, "f_probe_for_handover(" & log_label & "): Expected Handover, but got none. " & log_label & ": " & log_descr); } else { setverdict(pass); log("f_probe_for_handover(" & log_label & "): Got no Handover, as expected."); } log("f_probe_for_handover(" & log_label & "): done (got no Handover)"); break; } } f_rslem_resume(RSL1_PROC); f_rslem_resume(RSL2_PROC); f_sleep(3.0); RSL.clear; log("f_probe_for_handover(" & log_label & "): done clearing"); } /* Test the effect of various neighbor configuration scenarios: * * To avoid complexity, block off any actual handover operation, and always remain on the lchan at bts 0. * Reconfigure the neighbors for bts 0, trigger a Handover, and probe whether osmo-bsc does or doesn't start HO. */ private function f_tc_ho_neighbor_config_start() runs on MSC_ConnHdlr { g_pars := f_gen_test_hdlr_pars(); var template PDU_BSSAP exp_compl := f_gen_exp_compl(); var PDU_BSSAP ass_cmd := f_gen_ass_req(); const OCT8 kc := '0001020304050607'O; ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); /* Establish lchan at bts 0 */ f_establish_fully(ass_cmd, exp_compl); /* Shorten the inter-BSC Handover timeout, to not wait so long for inter-BSC Handovers */ f_vty_enter_cfg_network(BSCVTY); f_vty_transceive(BSCVTY, "timer T7 1"); f_vty_transceive(BSCVTY, "end"); } private function f_tc_ho_neighbor_config_1(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 1. No 'neighbor' config"); f_bts_0_cfg(BSCVTY, {"no neighbors"}); f_probe_for_handover("1.a", "HO to bts 1 works, implicitly listed as neighbor (legacy behavior when none are configured)", "handover any to arfcn 871 bsic 11", true); f_probe_for_handover("1.b", "HO to unknown cell does not start", "handover any to arfcn 13 bsic 39", false); f_probe_for_handover("1.c", "HO to 871-12 is ambiguous = error", "handover any to arfcn 871 bsic 12", false); f_probe_for_handover("1.d", "HO to 871-11 still works (verify that this test properly cleans up)", "handover any to arfcn 871 bsic 11", true); } testcase TC_ho_neighbor_config_1() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true, guard_timeout := 60.0); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_1)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 1.a */ /* "failed" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by sending a Handover Failure message. */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:failed"); /* 1.b */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); /* 1.c */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); /* 1.d */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:failed"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_2(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 2. explicit local neighbor: 'neighbor bts 1'"); f_bts_0_cfg(BSCVTY, {"neighbor bts 1"}); f_sleep(0.5); f_probe_for_handover("2.a", "HO to bts 1 works, explicitly listed as neighbor", "handover any to arfcn 871 bsic 11", true); f_probe_for_handover("2.b", "HO to bts 2 doesn't work, not listed as neighbor", "handover any to arfcn 871 bsic 12", false); } testcase TC_ho_neighbor_config_2() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true, guard_timeout := 50.0); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_2)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 2.a */ /* "failed" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by sending a Handover Failure message. */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:failed"); /* 2.b */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_3(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 3. explicit local neighbor: 'neighbor bts 2'"); f_bts_0_cfg(BSCVTY, {"no neighbors", "neighbor bts 2"}); f_sleep(0.5); f_probe_for_handover("3.a", "HO to bts 1 doesn't work, not listed as neighbor", "handover any to arfcn 871 bsic 11", false); f_probe_for_handover("3.b", "HO to bts 2 works, explicitly listed as neighbor; no ambiguity because bts 3 is not listed as neighbor", "handover any to arfcn 871 bsic 12", true); } testcase TC_ho_neighbor_config_3() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true, guard_timeout := 50.0); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_3)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 3.a */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); /* 3.b */ /* "failed" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by sending a Handover Failure message. */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:failed"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_4(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 4. explicit remote neighbor: 'neighbor lac 99 arfcn 123 bsic 45'"); f_bts_0_cfg(BSCVTY, {"no neighbors", "neighbor lac 99 arfcn 123 bsic 45"}); f_sleep(0.5); f_probe_for_handover("4.a", "HO to bts 1 doesn't work, not listed as neighbor", "handover any to arfcn 871 bsic 11", false); f_probe_for_handover("4.b", "HO to bts 2 doesn't work, not listed as neighbor", "handover any to arfcn 871 bsic 12", false); f_probe_for_handover("4.c", "HO to 123-45 triggers inter-BSC HO", "handover any to arfcn 123 bsic 45", true, true); } testcase TC_ho_neighbor_config_4() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true, guard_timeout := 50.0); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_4)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 4.a */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); /* 4.b */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); /* 4.c */ /* "timeout" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by timing out after the Handover Required message */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:timeout"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:timeout"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_5(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 5. explicit remote neighbor re-using ARFCN+BSIC: 'neighbor lac 99 arfcn 871 bsic 12'"); f_bts_0_cfg(BSCVTY, {"no neighbors", "neighbor lac 99 arfcn 871 bsic 12"}); f_sleep(0.5); f_probe_for_handover("5.a", "HO to 871-12 triggers inter-BSC HO (ignoring local cells with same ARFCN+BSIC)", "handover any to arfcn 871 bsic 12", true, true); } testcase TC_ho_neighbor_config_5() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_5)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 5 */ /* "timeout" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by timing out after the Handover Required message */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:timeout"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:timeout"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_6(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 6. config error: explicit local and remote neighbors with ambiguous ARFCN+BSIC:" & " 'neighbor bts 2; neighbor lac 99 arfcn 871 bsic 12'"); f_bts_0_cfg(BSCVTY, {"no neighbors", "neighbor bts 2", "neighbor lac 99 arfcn 871 bsic 12"}); f_sleep(0.5); f_probe_for_handover("6.a", "HO to 871-12 is ambiguous = error", "handover any to arfcn 871 bsic 12", false); } testcase TC_ho_neighbor_config_6() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_6)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 6.a */ /* "timeout" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by timing out after the Handover Required message */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:error"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } private function f_tc_ho_neighbor_config_7(charstring id) runs on MSC_ConnHdlr { f_tc_ho_neighbor_config_start(); /* * bts 0 ARFCN 871 BSIC 10 * bts 1 ARFCN 871 BSIC 11 * bts 2 ARFCN 871 BSIC 12 * bts 3 ARFCN 871 BSIC 12 serves as ambiguity for bts 2, re-using the ARFCN+BSIC */ log("f_tc_ho_neighbor_config: 7. explicit local and remote neighbors:" & " 'neighbor bts 2; neighbor lac 99 arfcn 123 bsic 45'"); f_bts_0_cfg(BSCVTY, {"no neighbors", "neighbor bts 2", "neighbor lac 99 arfcn 123 bsic 45"}); f_sleep(0.5); f_probe_for_handover("7.a", "HO to 871-12 does HO to bts 2", "handover any to arfcn 871 bsic 12", true); f_probe_for_handover("7.b", "HO to 123-45 triggers inter-BSC HO", "handover any to arfcn 123 bsic 45", true, true); } testcase TC_ho_neighbor_config_7() runs on test_CT { var MSC_ConnHdlr vc_conn; f_init(3, true, guard_timeout := 50.0); f_sleep(1.0); f_ctrs_bsc_and_bts_init(); vc_conn := f_start_handler(refers(f_tc_ho_neighbor_config_7)); vc_conn.done; /* f_tc_ho_neighbor_config_start() */ f_ctrs_bsc_and_bts_add(0, "assignment:attempted"); f_ctrs_bsc_and_bts_add(0, "assignment:completed"); /* 7.a */ /* "failed" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by sending a Handover Failure message. */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:failed"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:attempted"); f_ctrs_bsc_and_bts_add(0, "intra_bsc_ho:failed"); /* 7.b */ /* "timeout" means a handover was triggered and started (which is all this test aims for) and the test ended the * handover quickly by timing out after the Handover Required message */ f_ctrs_bsc_and_bts_add(0, "handover:attempted"); f_ctrs_bsc_and_bts_add(0, "handover:timeout"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:attempted"); f_ctrs_bsc_and_bts_add(0, "interbsc_ho_out:timeout"); f_ctrs_bsc_and_bts_verify(); f_shutdown_helper(); } /* OS#3041: Open and close N connections in a normal fashion, and expect no * BSSMAP Reset just because of that. */ testcase TC_bssap_rlsd_does_not_cause_bssmap_reset() runs on test_CT { var default d; var integer i; var DchanTuple dt; f_init(); /* Wait for initial BSSMAP Reset to pass */ f_sleep(4.0); d := activate(no_bssmap_reset()); /* Setup up a number of connections and RLSD them again from the MSC * side. In the buggy behavior, the fourth one triggers BSSMAP Reset. * Let's do it some more times for good measure. */ for (i := 0; i < 4; i := i+1) { /* Since we're doing a lot of runs, give each one a fresh * T_guard from the top. */ T_guard.start; /* Setup a BSSAP connection and clear it right away. This is * the MSC telling the BSC about a planned release, it's not an * erratic loss of a connection. */ dt := f_est_dchan(int2oct(i,1), 23+i, '00010203040506'O); /* MSC disconnects (RLSD). */ BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0)); } /* In the buggy behavior, a timeout of 2 seconds happens between above * trigger (logs "SIGTRAN connection down, reconnecting...") and the * actual BSSMAP Reset. Wait a bit longer just to make sure. */ f_sleep(4.0); deactivate(d); f_shutdown_helper(); } /* OS#3041: Open and close N connections in a normal fashion, and expect no * BSSMAP Reset just because of that. Invoke the release by a BSSMAP Clear from * the MSC. */ testcase TC_bssmap_clear_does_not_cause_bssmap_reset() runs on test_CT { var default d; var integer i; var DchanTuple dt; var BSSAP_N_DATA_ind rx_di; var myBSSMAP_Cause cause_val := GSM0808_CAUSE_CALL_CONTROL; var BssmapCause cause := enum2int(cause_val); f_init(); /* Wait for initial BSSMAP Reset to pass */ f_sleep(4.0); d := activate(no_bssmap_reset()); /* Setup up a number of connections and RLSD them again from the MSC * side. In the buggy behavior, the fourth one triggers BSSMAP Reset. * Let's do it some more times for good measure. */ for (i := 0; i < 8; i := i+1) { /* Since we're doing a lot of runs, give each one a fresh * T_guard from the top. */ T_guard.start; /* Setup a BSSAP connection and clear it right away. This is * the MSC telling the BSC about a planned release, it's not an * erratic loss of a connection. */ dt := f_est_dchan(int2oct(i,1), 23+i, '00010203040506'O); /* Instruct BSC to clear channel */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(cause))); /* expect BSC to disable the channel */ f_exp_chan_rel_and_clear(dt, 0); } /* In the buggy behavior, a timeout of 2 seconds happens between above * trigger (logs "SIGTRAN connection down, reconnecting...") and the * actual BSSMAP Reset. Wait a bit longer just to make sure. */ f_sleep(4.0); deactivate(d); f_shutdown_helper(); } /* OS#3041: Open and close N connections in a normal fashion, and expect no * BSSMAP Reset just because of that. Close connections from the MS side with a * Release Ind on RSL. */ testcase TC_ms_rel_ind_does_not_cause_bssmap_reset() runs on test_CT { var default d; var integer i; var DchanTuple dt; var BSSAP_N_DATA_ind rx_di; var integer j; f_init(); /* Wait for initial BSSMAP Reset to pass */ f_sleep(4.0); d := activate(no_bssmap_reset()); /* Setup up a number of connections and RLSD them again from the MSC * side. In the buggy behavior, the fourth one triggers BSSMAP Reset. * Let's do it some more times for good measure. */ for (i := 0; i < 8; i := i+1) { /* Since we're doing a lot of runs, give each one a fresh * T_guard from the top. */ T_guard.start; /* Setup a BSSAP connection and clear it right away. This is * the MSC telling the BSC about a planned release, it's not an * erratic loss of a connection. */ dt := f_est_dchan('23'O, 23, '00010203040506'O); /* simulate RLL REL IND */ f_ipa_tx(0, ts_RSL_REL_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)))); /* expect Clear Request on MSC side */ BSSAP.receive(tr_BSSAP_DATA_ind(dt.sccp_conn_id, tr_BSSMAP_ClearRequest)) -> value rx_di; /* Instruct BSC to clear channel */ var BssmapCause cause := bit2int(rx_di.userData.pdu.bssmap.clearRequest.cause.causeValue); BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommand(cause))); /* expect BSC to disable the channel */ f_exp_chan_rel_and_clear(dt, 0); } /* In the buggy behavior, a timeout of 2 seconds happens between above * trigger (logs "SIGTRAN connection down, reconnecting...") and the * actual BSSMAP Reset. Wait a bit longer just to make sure. */ f_sleep(4.0); deactivate(d); f_shutdown_helper(); } /*********************************************************************** * IPA style dynamic PDCH ***********************************************************************/ private function f_dyn_ipa_pdch_act(integer bts_nr, integer trx_nr, integer ts_nr, template (omit) RSL_Cause nack := omit) runs on test_CT { var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(ts_nr)); var RSL_Message rsl_unused; /* ask BSC via VTY to activate a given IPA style chan as PDCH */ f_vty_ts_action("pdch activate", bts_nr, trx_nr, ts_nr); /* expect the BSC to issue the related RSL command */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_IPA_PDCH_ACT(chan_nr)); if (istemplatekind(nack, "omit")) { /* respond with a related acknowledgement */ f_ipa_tx(0, ts_RSL_IPA_PDCH_ACT_ACK(chan_nr, ts_RSL_IE_FrameNumber(2342))); } else { f_ipa_tx(0, ts_RSL_IPA_PDCH_ACT_NACK(chan_nr, valueof(nack))); } } private function f_dyn_ipa_pdch_deact(integer bts_nr, integer trx_nr, integer ts_nr, template (omit) RSL_Cause nack := omit) runs on test_CT { var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(ts_nr)); var RSL_Message rsl_unused; /* ask BSC via VTY to activate a given IPA style chan as PDCH */ f_vty_ts_action("pdch deactivate", bts_nr, trx_nr, ts_nr); /* expect the BSC to issue the related RSL command */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_IPA_PDCH_DEACT(chan_nr)); if (istemplatekind(nack, "omit")) { /* respond with a related acknowledgement */ f_ipa_tx(0, ts_RSL_IPA_PDCH_DEACT_ACK(chan_nr)); } else { f_ipa_tx(0, ts_RSL_IPA_PDCH_DEACT_NACK(chan_nr, valueof(nack))); } } private function f_ts_dyn_mode_get(integer bts_nr, integer trx_nr, integer ts_nr) runs on test_CT return charstring { var charstring cmd, resp; cmd := "show timeslot "&int2str(bts_nr)&" "&int2str(trx_nr)&" "&int2str(ts_nr); return f_vty_transceive_match_regexp_retry(BSCVTY, cmd, "*\((*)\)*", 0, 4, 1.0); } private function f_ts_dyn_mode_assert(integer bts_nr, integer trx_nr, integer ts_nr, template charstring exp) runs on test_CT { var charstring mode := f_ts_dyn_mode_get(bts_nr, trx_nr, ts_nr); if (not match(mode, exp)) { setverdict(fail, "Unexpected TS Mode: ", mode); mtc.stop; } } private function f_ts_set_chcomb(integer bts_nr, integer trx_nr, integer ts_nr, charstring chcomb) runs on test_CT { f_vty_enter_cfg_ts(BSCVTY, bts_nr, trx_nr, ts_nr); f_vty_transceive(BSCVTY, "phys_chan_config " & chcomb); f_vty_transceive(BSCVTY, "end"); } private const charstring TCHF_MODE := "TCH/F mode"; private const charstring TCHH_MODE := "TCH/H mode"; private const charstring PDCH_MODE := "PDCH mode"; private const charstring NONE_MODE := "NONE mode"; /* Test IPA PDCH activation / deactivation triggered by VTY */ testcase TC_dyn_pdch_ipa_act_deact() runs on test_CT { var RSL_Message rsl_unused; /* change Timeslot 6 before f_init() starts RSL */ f_init_vty(); f_ts_set_chcomb(0, 0, 6, "TCH/F_PDCH"); f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1, false); f_sleep(1.0); var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(6)); log("TCH/F_PDCH pchan starts out in TCH/F mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* The BSC will activate the dynamic PDCH by default, so confirm that */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_IPA_PDCH_ACT(chan_nr)); f_ipa_tx(0, ts_RSL_IPA_PDCH_ACT_ACK(chan_nr, ts_RSL_IE_FrameNumber(2342))); f_sleep(1.0); log("TCH/F_PDCH pchan, PDCH ACT was ACKed, so now in PDCH mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, PDCH_MODE); /* De-activate it via VTY */ f_dyn_ipa_pdch_deact(0, 0, chan_nr.tn); f_sleep(1.0); log("TCH/F_PDCH pchan, PDCH DEACT via VTY, so now back in TCH/F mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* re-activate it via VTY */ f_dyn_ipa_pdch_act(0, 0, chan_nr.tn); f_sleep(1.0); log("TCH/F_PDCH pchan, PDCH ACT via VTY, so now in PDCH mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, PDCH_MODE); /* and finally de-activate it again */ f_dyn_ipa_pdch_deact(0, 0, chan_nr.tn); f_sleep(1.0); log("TCH/F_PDCH pchan, PDCH DEACT via VTY, so now back in TCH/F mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* clean up config */ f_ts_set_chcomb(0, 0, 6, "PDCH"); f_shutdown_helper(); } /* Test IPA PDCH activation NACK */ testcase TC_dyn_pdch_ipa_act_nack() runs on test_CT { var RSL_Message rsl_unused; /* change Timeslot 6 before f_init() starts RSL */ f_init_vty(); f_ts_set_chcomb(0, 0, 6, "TCH/F_PDCH"); f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1, false); f_sleep(1.0); var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(6)); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* The BSC will activate the dynamic PDCH by default, so confirm that */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_IPA_PDCH_ACT(chan_nr)); f_ipa_tx(0, ts_RSL_IPA_PDCH_ACT_ACK(chan_nr, ts_RSL_IE_FrameNumber(2342))); f_sleep(1.0); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, PDCH_MODE); /* De-activate it via VTY */ f_dyn_ipa_pdch_deact(0, 0, chan_nr.tn); f_sleep(1.0); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* re-activate it via VTY, but fail that; check BSC still assumes TCH/F mode */ f_dyn_ipa_pdch_act(0, 0, chan_nr.tn, RSL_ERR_EQUIPMENT_FAIL); f_sleep(1.0); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, TCHF_MODE); /* clean up config */ f_ts_set_chcomb(0, 0, 6, "PDCH"); f_shutdown_helper(); } /*********************************************************************** * Osmocom style dynamic PDCH ***********************************************************************/ private function f_dyn_osmo_pdch_act(integer bts_nr, integer trx_nr, integer ts_nr, template (omit) RSL_Cause nack := omit) runs on test_CT { var RslChannelNr chan_nr := valueof(t_RslChanNr_PDCH(ts_nr)); var RSL_Message rsl_unused; /* ask BSC via VTY to activate a given IPA style chan as PDCH */ /* FIXME: no VTY command to activate Osmocom PDCH !! */ /* expect the BSC to issue the related RSL command */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_CHAN_ACT(chan_nr, ?)); if (istemplatekind(nack, "omit")) { /* respond with a related acknowledgement */ f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(chan_nr, 2342)); } else { f_ipa_tx(0, ts_RSL_CHAN_ACT_NACK(chan_nr, valueof(nack))); } } private function f_dyn_osmo_pdch_deact(integer bts_nr, integer trx_nr, integer ts_nr, template (omit) RSL_Cause nack := omit) runs on test_CT { var RslChannelNr chan_nr := valueof(t_RslChanNr_PDCH(ts_nr)); var RSL_Message rsl_unused; /* ask BSC via VTY to activate a given IPA style chan as PDCH */ /* FIXME: no VTY command to activate Osmocom PDCH !! */ /* expect the BSC to issue the related RSL command */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_RF_CHAN_REL(chan_nr)); if (istemplatekind(nack, "omit")) { /* respond with a related acknowledgement */ f_ipa_tx(0, ts_RSL_RF_CHAN_REL_ACK(chan_nr)); } else { //f_ipa_tx(0, ts_RSL_RF_CHAN_REL_NACK(chan_nr, valueof(nack))); } } /* Test Osmocom dyn PDCH activation / deactivation triggered by VTY */ testcase TC_dyn_pdch_osmo_act_deact() runs on test_CT { var RSL_Message rsl_unused; /* change Timeslot 6 before f_init() starts RSL */ f_init_vty(); f_ts_set_chcomb(0, 0, 6, "TCH/F_TCH/H_PDCH"); f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1, false); f_sleep(1.0); var RslChannelNr chan_nr := valueof(t_RslChanNr_PDCH(6)); log("TCH/F_TCH/H_PDCH pchan starts out in disabled mode:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, NONE_MODE); /* The BSC will activate the dynamic PDCH by default, so confirm that */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_CHAN_ACT_PDCH(chan_nr, ?)); f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(chan_nr, 2342)); f_sleep(1.0); log("TCH/F_TCH/H_PDCH requested to PDCH ACT on startup, which was ACKed, so now in PDCH:"); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, PDCH_MODE); /* clean up config */ f_ts_set_chcomb(0, 0, 6, "PDCH"); f_shutdown_helper(); } /* Test Osmocom dyn PDCH activation NACK behavior */ testcase TC_dyn_pdch_osmo_act_nack() runs on test_CT { var RSL_Message rsl_unused; /* change Timeslot 6 before f_init() starts RSL */ f_init_vty(); f_ts_set_chcomb(0, 0, 6, "TCH/F_TCH/H_PDCH"); f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1, false); f_sleep(1.0); var RslChannelNr chan_nr := valueof(t_RslChanNr_PDCH(6)); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, NONE_MODE); /* The BSC will activate the dynamic PDCH by default, so confirm that */ rsl_unused := f_exp_ipa_rx(0, tr_RSL_CHAN_ACT_PDCH(chan_nr, ?)); /* NACK this activation and expect the "show timeslot" mode still to be NONE */ f_ipa_tx(0, ts_RSL_CHAN_ACT_NACK(chan_nr, RSL_ERR_EQUIPMENT_FAIL)); f_sleep(1.0); f_ts_dyn_mode_assert(0, 0, chan_nr.tn, NONE_MODE); /* clean up config */ f_ts_set_chcomb(0, 0, 6, "PDCH"); f_shutdown_helper(); } testcase TC_chopped_ipa_ping() runs on test_CT { const Integers bsc_ipa_ports := {mp_bsc_rsl_port, mp_bsc_oml_port, mp_bsc_ctrl_port}; for (var integer i := 0; i < lengthof(bsc_ipa_ports); i := i + 1) { IPA_Testing.f_run_TC_chopped_ipa_ping(mp_bsc_ip, bsc_ipa_ports[i], CONNECT_TO_SERVER); } f_shutdown_helper(); } testcase TC_chopped_ipa_payload() runs on test_CT { const Integers bsc_ipa_ports := {mp_bsc_rsl_port, mp_bsc_oml_port /* TODO: mp_bsc_ctrl_port does not work yet */}; for (var integer i := 0; i < lengthof(bsc_ipa_ports); i := i + 1) { IPA_Testing.f_run_TC_chopped_ipa_payload(mp_bsc_ip, bsc_ipa_ports[i], CONNECT_TO_SERVER); } f_shutdown_helper(); } /* Verify the BSC sends the MS Power Parameters IE during CHAN ACT to make sure the BTS does autonomous MS power control loop */ testcase TC_assignment_verify_ms_power_params_ie() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); //pars.encr := valueof(t_EncrParams('01'O, f_rnd_octstring(8))); pars.exp_ms_power_params := true; f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_assignment_fr_a5), pars); vc_conn.done; f_shutdown_helper(); } /*********************************************************************** * MSC Pooling ***********************************************************************/ function f_tmsi_nri(integer nri_v, octetstring base_tmsi := '42000023'O, integer nri_bitlen := 10) return octetstring { return int2oct( oct2int(base_tmsi) + bit2int( (int2bit(nri_v, 32) << ( 24 - nri_bitlen)) ), 4); } template MobileIdentityLV ts_MI_TMSI_NRI_LV(integer nri_v, integer nri_bitlen := 10) := ts_MI_TMSI_LV(tmsi := f_tmsi_nri(nri_v, nri_bitlen := nri_bitlen)); private function f_expect_lchan_rel(RSL_DCHAN_PT rsl) runs on MSC_ConnHdlr { interleave { [] rsl.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_RR_RELEASE)) { f_logp(BSCVTY, "Got RSL RR Release"); } [] rsl.receive(tr_RSL_DEACT_SACCH(g_chan_nr)) { f_logp(BSCVTY, "Got RSL Deact SACCH"); } [] rsl.receive(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL)) { f_logp(BSCVTY, "Got RSL RF Chan Rel, sending Rel Ack"); rsl.send(ts_RSL_RF_CHAN_REL_ACK(g_chan_nr)); break; } } } private function f_perform_clear(RSL_DCHAN_PT rsl) runs on MSC_ConnHdlr { f_logp(BSCVTY, "MSC instructs BSC to clear channel"); BSSAP.send(ts_BSSMAP_ClearCommand(0)); interleave { [] rsl.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_RR_RELEASE)) { f_logp(BSCVTY, "Got RSL RR Release"); } [] rsl.receive(tr_RSL_DEACT_SACCH(g_chan_nr)) { f_logp(BSCVTY, "Got RSL Deact SACCH"); } [] BSSAP.receive(tr_BSSMAP_ClearComplete) { f_logp(BSCVTY, "Got BSSMAP Clear Complete"); /* Also drop the SCCP connection */ BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ); } [] rsl.receive(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL)) { f_logp(BSCVTY, "Got RSL RF Chan Rel, sending Rel Ack"); rsl.send(ts_RSL_RF_CHAN_REL_ACK(g_chan_nr)); } } } private function f_perform_compl_l3(RSL_DCHAN_PT rsl, template PDU_ML3_MS_NW l3_info, boolean do_clear := true, boolean expect_bssmap_l3 := true) runs on MSC_ConnHdlr { timer T := 10.0; var octetstring l3_enc := enc_PDU_ML3_MS_NW(valueof(l3_info)); f_logp(BSCVTY, "establish channel, send Complete Layer 3 Info"); f_create_bssmap_exp(l3_enc); /* RSL_Emulation.f_chan_est() on rsl: * This is basically code dup with s/RSL/rsl from: * RSL_Emulation.f_chan_est(g_pars.ra, l3_enc, g_pars.link_id, g_pars.fn); */ var RSL_Message rx_rsl; var GsmRrMessage rr; /* request a channel to be established */ rsl.send(ts_RSLDC_ChanRqd(g_pars.ra, g_pars.fn)); /* expect immediate assignment. * Code dup with s/RSL/rsl from: * rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN); */ timer Tt := 10.0; /* request a channel to be established */ Tt.start; alt { [] rsl.receive(tr_RSL_IMM_ASSIGN) -> value rx_rsl { Tt.stop; } [] rsl.receive { setverdict(fail, "Unexpected RSL message on DCHAN"); mtc.stop; } [] Tt.timeout { setverdict(fail, "Timeout waiting for RSL on DCHAN"); mtc.stop; } } rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload); g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr; rsl.send(ts_RSL_EST_IND(g_chan_nr, valueof(g_pars.link_id), l3_enc)); if (expect_bssmap_l3) { f_logp(BSCVTY, "expect BSSAP Complete Layer 3 Info at MSC"); var template PDU_BSSAP exp_l3_compl; exp_l3_compl := tr_BSSMAP_ComplL3() if (g_pars.aoip == false) { exp_l3_compl.pdu.bssmap.completeLayer3Information.codecList := omit; } else { exp_l3_compl.pdu.bssmap.completeLayer3Information.codecList := ?; } var PDU_BSSAP bssap; T.start; alt { [] BSSAP.receive(exp_l3_compl) -> value bssap { f_logp(BSCVTY, "received expected Complete Layer 3 Info at MSC"); log("rx exp_l3_compl = ", bssap); } [] BSSAP.receive(tr_BSSMAP_ComplL3) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received non-matching COMPLETE LAYER 3 INFORMATION"); } [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for COMPLETE LAYER 3 INFORMATION"); } } /* start ciphering, if requested */ if (ispresent(g_pars.encr)) { f_logp(BSCVTY, "start ciphering"); f_cipher_mode(g_pars.encr.enc_alg, g_pars.encr.enc_key); } } if (do_clear) { f_perform_clear(rsl); } setverdict(pass); f_sleep(1.0); } private function f_tc_mscpool_compl_l3(charstring id) runs on MSC_ConnHdlr { f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); if (g_pars.mscpool.rsl_idx == 0) { f_perform_compl_l3(RSL, g_pars.mscpool.l3_info); } else if (g_pars.mscpool.rsl_idx == 1) { f_perform_compl_l3(RSL1, g_pars.mscpool.l3_info); } else if (g_pars.mscpool.rsl_idx == 2) { f_perform_compl_l3(RSL2, g_pars.mscpool.l3_info); } } /* Various Complete Layer 3 by IMSI all end up with the first MSC, because the other MSCs are not connected. */ private function f_tc_mscpool_L3Compl_on_1_msc(charstring id) runs on MSC_ConnHdlr { f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); f_perform_compl_l3(RSL, ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_IMSI_LV('001010000000001'H)), '00F110'O) ); f_perform_compl_l3(RSL, ts_CM_SERV_REQ(CM_TYPE_MO_SMS, valueof(ts_MI_IMSI_LV('001010000000002'H))) ); f_perform_compl_l3(RSL, ts_PAG_RESP(valueof(ts_MI_IMSI_LV('001010000000003'H))) ); f_perform_compl_l3(RSL, ts_ML3_MO_MM_IMSI_DET_Ind(valueof(ts_MI_IMSI_LV('001010000000004'H))) ); } testcase TC_mscpool_L3Compl_on_1_msc() runs on test_CT { f_init(1, true); f_sleep(1.0); var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_ctrs_msc_init(); vc_conn := f_start_handler(refers(f_tc_mscpool_L3Compl_on_1_msc), pars); vc_conn.done; f_ctrs_msc_expect(0, "mscpool:subscr:new", 4); f_shutdown_helper(); } /* Three Layer 3 Complete by IMSI are round-robin'ed across two connected MSCs */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_L3Complete_by_imsi_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_IMSI_LV('001010000000001'H)), '00F110'O)); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_IMSI_LV('001010000000002'H)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(1, "mscpool:subscr:new"); /* Test round-robin wrap to the first MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_PAG_RESP(valueof(ts_MI_IMSI_LV('001010000000003'H)))); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); f_shutdown_helper(); } /* Three LU by TMSI are round-robin'ed across two connected MSCs, because they contain the NULL-NRI 0 * (configured in osmo-bsc.cfg). */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_LU_by_tmsi_null_nri_0_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(0)), '00F110'O)); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:reattach"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(0)), '00F110'O)); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(1, "mscpool:subscr:reattach"); /* Test round-robin wrap to the first MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(0)), '00F110'O)); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(0, "mscpool:subscr:reattach"); f_shutdown_helper(); } /* Three LU by TMSI are round-robin'ed across two connected MSCs, because they contain the NULL-NRI 1 * (configured in osmo-bsc.cfg). In this case, one of the MSC also has the NULL-NRI as part of its owned NRIs, but the * NULL-NRI setting is stronger than that. */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_LU_by_tmsi_null_nri_1_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(1)), '00F110'O)); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:reattach"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(1)), '00F110'O)); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(1, "mscpool:subscr:reattach"); /* Test round-robin wrap to the first MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(1)), '00F110'O)); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(0, "mscpool:subscr:reattach"); f_shutdown_helper(); } /* Three Layer 3 Complete by TMSI are round-robin'ed across two connected MSCs, because they contain an NRI not * assigned to any MSC (configured in osmo-bsc.cfg). */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_L3Complete_by_tmsi_unassigned_nri_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; /* An NRI that is not assigned to any MSC */ pars1.mscpool.l3_info := valueof(ts_ML3_MO_MM_IMSI_DET_Ind(valueof(ts_MI_TMSI_NRI_LV(1023)))); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; /* An NRI that is not assigned to any MSC */ pars2.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(768)), '00F110'O)); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(1, "mscpool:subscr:new"); /* Test round-robin wrap to the first MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; /* An NRI that is not assigned to any MSC */ pars3.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_SS_ACT, valueof(ts_MI_TMSI_NRI_LV(819)))); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); f_shutdown_helper(); } /* Three Layer 3 Complete by TMSI are round-robin'ed across two connected MSCs, because they contain an NRI * assigned to an MSC that is currently not connected (configured in osmo-bsc.cfg). */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; /* An NRI that is assigned to an unconnected MSC */ pars1.mscpool.l3_info := valueof(ts_PAG_RESP(valueof(ts_MI_TMSI_NRI_LV(512)))); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_add(2, "mscpool:subscr:attach_lost"); f_ctrs_msc_add(0, "mscpool:subscr:new"); f_ctrs_msc_verify(); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; /* An NRI that is assigned to an unconnected MSC */ pars2.mscpool.l3_info := valueof(ts_ML3_MO_MM_IMSI_DET_Ind(valueof(ts_MI_TMSI_NRI_LV(767)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_add(2, "mscpool:subscr:attach_lost"); f_ctrs_msc_add(1, "mscpool:subscr:new"); f_ctrs_msc_verify(); /* Test round-robin wrap to the first MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; /* An NRI that is assigned to an unconnected MSC */ pars3.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(750)), '00F110'O)); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_add(2, "mscpool:subscr:attach_lost"); f_ctrs_msc_add(0, "mscpool:subscr:new"); f_ctrs_msc_verify(); f_shutdown_helper(); } /* Three Layer 3 Complete by TMSI with valid NRI for the second MSC are all directed to the second MSC (configured in * osmo-bsc.cfg). */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_1() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 2); f_sleep(1.0); /* All TMSIs in this test point at the second MSC, set the round robin to point at the first MSC to make sure * this is not using round-robin. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 1); pars1.mscpool.rsl_idx := 0; /* An NRI of the second MSC's range (256-511) */ pars1.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_SMS, valueof(ts_MI_TMSI_NRI_LV(256)))); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(1, "mscpool:subscr:known"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 1); pars2.mscpool.rsl_idx := 1; /* An NRI of the second MSC's range (256-511) */ pars2.mscpool.l3_info := valueof(ts_PAG_RESP(valueof(ts_MI_TMSI_NRI_LV(260)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(1, "mscpool:subscr:known"); var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 1); pars3.mscpool.rsl_idx := 2; /* An NRI of the second MSC's range (256-511) */ pars3.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(511)), '00F110'O)); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(1, "mscpool:subscr:known"); f_shutdown_helper(); } /* Layer 3 Complete by TMSI with valid NRI for the third MSC are directed to the third MSC (configured in osmo-bsc.cfg), * while a round-robin remains unaffected by that. */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_2() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* All TMSIs in this test point at the third MSC, set the round robin to point at the second MSC to make sure * this is not using round-robin. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 1"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 2); pars1.mscpool.rsl_idx := 0; /* An NRI of the third MSC's range (512-767) */ pars1.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_SMS, valueof(ts_MI_TMSI_NRI_LV(512)))); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(2, "mscpool:subscr:known"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 2); pars2.mscpool.rsl_idx := 1; /* An NRI of the third MSC's range (512-767) */ pars2.mscpool.l3_info := valueof(ts_PAG_RESP(valueof(ts_MI_TMSI_NRI_LV(678)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(2, "mscpool:subscr:known"); /* The above forwardings to third MSC have not affected the round robin, which still points at the second MSC */ var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 1); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_IMSI_LV('001010000000013'H)), '00F110'O)); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(1, "mscpool:subscr:new"); f_shutdown_helper(); } /* LU with a TMSI but indicating a different PLMN in its previous LAI: ignore the NRI. */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_LU_by_tmsi_from_other_PLMN() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* The TMSIs in this test points at the second MSC, but since it is from a different PLMN, round-robin is used * instead, and hits msc 0. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); /* An NRI of the second MSC's range (256-511), but a PLMN that doesn't match with osmo-bsc.cfg */ var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(260)), '99F999'O)); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); /* An NRI of the third MSC's range (512-767) and a matching PLMN gets directed by NRI. */ var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 2); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_TMSI_NRI_LV(555)), '00F110'O)); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(2, "mscpool:subscr:known"); f_shutdown_helper(); } /* Make sure that whichever MSC paged a subscriber will also get the Paging Response. Page by IMSI, which would be * round-robined to another MSC, to make sure the Paging->Response relation is stronger than the NRI->MSC mapping. */ private function f_tc_mscpool_paging_imsi(charstring id) runs on MSC_ConnHdlr { var template BSSMAP_FIELD_CellIdentificationList cid_list := { cIl_CI := { ts_BSSMAP_CI_CI(0) } }; //cid_list := { cIl_allInBSS := ''O }; var RSL_ChanNeeded rsl_chneed := RSL_CHANNEED_SDCCH; var template BSSMAP_IE_ChannelNeeded bssmap_chneed := ts_BSSMAP_IE_ChanNeeded(int2bit(enum2int(valueof(rsl_chneed)),2)); var BSSAP_N_UNITDATA_req paging; var hexstring imsi := '001010000000123'H; f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); paging := valueof(ts_BSSAP_UNITDATA_req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Paging(imsi, cid_list, omit, bssmap_chneed)))); BSSAP.send(paging); /* Register any RSL conn so that the Paging Command gets received here. With the current RSL_Emulation's main() * handling of '[bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD()))' it doesn't matter at all which * channel number is picked here. */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(0, RSL_CHAN_NR_INVALID)); f_rslem_register(0, new_chan_nr); RSL.receive(tr_RSL_PAGING_CMD(tr_MI_IMSI(imsi))); f_rslem_unregister(0, new_chan_nr); /* Despite the round robin pointing at the second MSC ('roundrobin next 1'), the earlier Paging for the same IMSI * causes this Paging Response to go to the first MSC (bssap_idx := 0). */ f_perform_compl_l3(RSL, ts_PAG_RESP(valueof(ts_MI_IMSI_LV(imsi))) ); f_sleep(1.0); } testcase TC_mscpool_paging_and_response_imsi() runs on test_CT { f_init(nr_bts := 1, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* Testing a Paging on the first MSC to get a Paging Response back to the first MSC. Set round robin to the * second MSC to make sure we're getting the Paging logic, not a coincidental round robin match. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 1"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.sccp_addr_bsc := g_bssap[pars1.mscpool.bssap_idx].sccp_addr_peer; pars1.sccp_addr_msc := g_bssap[pars1.mscpool.bssap_idx].sccp_addr_own; vc_conn1 := f_start_handler(refers(f_tc_mscpool_paging_imsi), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:paged"); f_shutdown_helper(); } /* Make sure that whichever MSC paged a subscriber will also get the Paging Response. Page by TMSI with an NRI value * that matches a different MSC, to make sure the Paging->Response relation is stronger than the NRI->MSC mapping. */ private function f_tc_mscpool_paging_tmsi(charstring id) runs on MSC_ConnHdlr { var template BSSMAP_FIELD_CellIdentificationList cid_list := { cIl_CI := { ts_BSSMAP_CI_CI(0) } }; //cid_list := { cIl_allInBSS := ''O }; var RSL_ChanNeeded rsl_chneed := RSL_CHANNEED_SDCCH; var template BSSMAP_IE_ChannelNeeded bssmap_chneed := ts_BSSMAP_IE_ChanNeeded(int2bit(enum2int(valueof(rsl_chneed)),2)); var integer nri_v := 300; /* <-- second MSC's NRI */ var octetstring tmsi := f_tmsi_nri(nri_v); var BSSAP_N_UNITDATA_req paging; f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); paging := valueof(ts_BSSAP_UNITDATA_req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Paging('001010000000011'H, cid_list, tmsi, bssmap_chneed)))); BSSAP.send(paging); /* Register any RSL conn so that the Paging Command gets received here. With the current RSL_Emulation's main() * handling of '[bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD()))' it doesn't matter at all which * channel number is picked here. */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(0, RSL_CHAN_NR_INVALID)); f_rslem_register(0, new_chan_nr); RSL.receive(tr_RSL_PAGING_CMD(t_MI_TMSI(tmsi))); f_rslem_unregister(0, new_chan_nr); /* Despite the NRI matching the second MSC (NRI from 'msc 1' in osmo-bsc.cfg) and round robin pointing at the * third MSC ('roundrobin next 2'), the earlier Paging for the same TMSI causes this Paging Response to go to * the first MSC (bssap_idx := 0). */ f_perform_compl_l3(RSL, ts_PAG_RESP(valueof(ts_MI_TMSI_NRI_LV(nri_v))) ); f_sleep(1.0); } testcase TC_mscpool_paging_and_response_tmsi() runs on test_CT { f_init(nr_bts := 1, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* Testing a Paging on the first MSC to get a Paging Response back to the first MSC. Set round robin to the * third MSC to make sure we're getting the Paging logic, not a coincidental round robin match. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 2"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.sccp_addr_bsc := g_bssap[pars1.mscpool.bssap_idx].sccp_addr_peer; pars1.sccp_addr_msc := g_bssap[pars1.mscpool.bssap_idx].sccp_addr_own; vc_conn1 := f_start_handler(refers(f_tc_mscpool_paging_tmsi), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:paged"); f_shutdown_helper(); } /* For round-robin, skip an MSC that has 'no allow-attach' set. */ /* FIXME: each run is using a separate RSLem: RSL, RSL1, RSL2. It should work * just as well using only RSL. */ testcase TC_mscpool_no_allow_attach_round_robin() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* Mark the second MSC as offloading, round-robin should skip this MSC now. */ f_vty_msc_allow_attach(BSCVTY, {true, false, true}); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 0); pars1.mscpool.rsl_idx := 0; pars1.mscpool.l3_info := valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(ts_MI_IMSI_LV('001010000000001'H)), '00F110'O)); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 2); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_IMSI_LV('001010000000002'H)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(2, "mscpool:subscr:new"); var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 0); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_PAG_RESP(valueof(ts_MI_IMSI_LV('001010000000003'H)))); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); f_shutdown_helper(); } /* An MSC that has 'no allow-attach' set should still serve subscribers that are already attached according to their * TMSI NRI. */ testcase TC_mscpool_no_allow_attach_valid_nri() runs on test_CT { f_init(nr_bts := 3, handler_mode := true, nr_msc := 3); f_sleep(1.0); /* Mark the second MSC as offloading, round-robin should skip this MSC now. */ f_vty_msc_allow_attach(BSCVTY, {true, false, true}); /* Control which MSC gets chosen next by the round-robin, otherwise * would be randomly affected by which other tests ran before this. */ f_vty_transceive(BSCVTY, "mscpool roundrobin next 0"); f_ctrs_msc_init(); /* Round robin points at msc 0, but the valid NRI directs to msc 1, even though msc 1 has 'no allow-attach'. */ var MSC_ConnHdlr vc_conn1; var TestHdlrParams pars1 := f_gen_test_hdlr_pars(bssap_idx := 1); pars1.mscpool.rsl_idx := 0; /* An NRI of the second MSC's range (256-511) */ pars1.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_TMSI_NRI_LV(260)))); vc_conn1 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars1); vc_conn1.done; f_ctrs_msc_expect(1, "mscpool:subscr:known"); var MSC_ConnHdlr vc_conn2; var TestHdlrParams pars2 := f_gen_test_hdlr_pars(bssap_idx := 0); pars2.mscpool.rsl_idx := 1; pars2.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_IMSI_LV('001010000000002'H)))); vc_conn2 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars2); vc_conn2.done; f_ctrs_msc_expect(0, "mscpool:subscr:new"); var MSC_ConnHdlr vc_conn3; var TestHdlrParams pars3 := f_gen_test_hdlr_pars(bssap_idx := 2); pars3.mscpool.rsl_idx := 2; pars3.mscpool.l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_IMSI_LV('001010000000003'H)))); vc_conn3 := f_start_handler(refers(f_tc_mscpool_compl_l3), pars3); vc_conn3.done; f_ctrs_msc_expect(2, "mscpool:subscr:new"); f_shutdown_helper(); } /* Allow/Deny emergency calls globally via VTY */ private function f_vty_allow_emerg_msc(boolean allow) runs on test_CT { f_vty_enter_cfg_msc(BSCVTY, 0); if (allow) { f_vty_transceive(BSCVTY, "allow-emergency allow"); } else { f_vty_transceive(BSCVTY, "allow-emergency deny"); } f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); } /* Allow/Deny emergency calls per BTS via VTY */ private function f_vty_allow_emerg_bts(boolean allow, integer bts_nr) runs on test_CT { f_vty_enter_cfg_bts(BSCVTY, bts_nr); if (allow) { f_vty_transceive(BSCVTY, "rach emergency call allowed 1"); } else { f_vty_transceive(BSCVTY, "rach emergency call allowed 0"); } f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); f_vty_transceive(BSCVTY, "exit"); } /* Begin assignmet procedure and send an EMERGENCY SETUP (RR) */ private function f_assignment_emerg_setup() runs on MSC_ConnHdlr { var PDU_ML3_MS_NW emerg_setup; var octetstring emerg_setup_enc; var RSL_Message emerg_setup_data_ind; f_establish_fully(omit, omit); emerg_setup := valueof(ts_ML3_MO_CC_EMERG_SETUP(1, valueof(ts_Bcap_voice))); emerg_setup_enc := enc_PDU_ML3_MS_NW(emerg_setup); emerg_setup_data_ind := valueof(ts_RSL_DATA_IND(g_chan_nr, valueof(ts_RslLinkID_DCCH(0)), emerg_setup_enc)); RSL.send(emerg_setup_data_ind); } /* Test if the EMERGENCY SETUP gets passed on to the MSC via A when EMERGENCY * CALLS are permitted by the BSC config. */ private function f_TC_assignment_emerg_setup_allow(charstring id) runs on MSC_ConnHdlr { var PDU_BSSAP emerg_setup_data_ind_bssap; var PDU_ML3_MS_NW emerg_setup; timer T := 3.0; f_assignment_emerg_setup() T.start; alt { [] BSSAP.receive(tr_BSSAP_DTAP) -> value emerg_setup_data_ind_bssap { emerg_setup := dec_PDU_ML3_MS_NW(emerg_setup_data_ind_bssap.pdu.dtap); if (not isbound(emerg_setup.msgs.cc.emergencySetup)) { setverdict(fail, "no emergency setup"); } } [] BSSAP.receive { setverdict(fail, "unexpected BSSAP message!"); } [] T.timeout { setverdict(fail, "timout waiting for EMERGENCY SETUP!"); } } setverdict(pass); } /* Test if the EMERGENCY SETUP gets blocked by the BSC if EMERGENCY CALLS are * forbidden by the BSC config. */ private function f_TC_assignment_emerg_setup_deny(charstring id) runs on MSC_ConnHdlr { var PDU_BSSAP emerg_setup_data_ind_bssap; timer T := 3.0; f_assignment_emerg_setup() T.start; alt { [] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr, ?, decmatch tr_RRM_RR_RELEASE)) { setverdict(pass); } [] RSL.receive { setverdict(fail, "unexpected RSL message!"); } [] T.timeout { setverdict(fail, "timout waiting for RR CHANNEL RELEASE!"); } } } /* EMERGENCY CALL situation #1, allowed globally and by BTS */ testcase TC_assignment_emerg_setup_allow() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_vty_allow_emerg_msc(true); f_vty_allow_emerg_bts(true, 0); vc_conn := f_start_handler(refers(f_TC_assignment_emerg_setup_allow), pars); vc_conn.done; f_shutdown_helper(); } /* EMERGENCY CALL situation #2, forbidden globally but allowed by BTS */ testcase TC_assignment_emerg_setup_deny_msc() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; f_init(1, true); f_sleep(1.0); f_vty_allow_emerg_msc(false); f_vty_allow_emerg_bts(true, 0); vc_conn := f_start_handler(refers(f_TC_assignment_emerg_setup_deny), pars); vc_conn.done; f_shutdown_helper(); } /* EMERGENCY CALL situation #3, allowed globally but forbidden by BTS */ testcase TC_assignment_emerg_setup_deny_bts() runs on test_CT { var TestHdlrParams pars := f_gen_test_hdlr_pars(); var MSC_ConnHdlr vc_conn; /* Note: This simulates a spec violation by the MS, correct MS * implementations would not try to establish an emergency call because * the system information tells in advance that emergency calls are * not forbidden */ f_init(1, true); f_sleep(1.0); f_vty_allow_emerg_msc(true); f_vty_allow_emerg_bts(false, 0); vc_conn := f_start_handler(refers(f_TC_assignment_emerg_setup_deny), pars); vc_conn.done; f_shutdown_helper(); } /* Test what happens when an emergency call arrives while all TCH channels are * busy, the BSC is expected to terminate one call in favor of the incoming * emergency call */ testcase TC_emerg_premption() runs on test_CT { var ASP_RSL_Unitdata rsl_ud; var integer i; var integer chreq_total, chreq_nochan; var RSL_Message rx_rsl; var RslChannelNr chan_nr; f_init(1); f_sleep(1.0); f_vty_allow_emerg_msc(true); f_vty_allow_emerg_bts(true, 0); /* Fill up all channels on the BTS */ chreq_total := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total"); chreq_nochan := f_ctrl_get_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:no_channel"); for (i := 0; i < NUM_TCHF_PER_BTS + NUM_TCHH_PER_BTS + NUM_SDCCH_PER_BTS; i := i+1) { chan_nr := f_chreq_act_ack('33'O, i); } IPA_RSL[0].clear; f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bts", 0, "chreq:total", chreq_total + NUM_TCHF_PER_BTS + NUM_TCHH_PER_BTS + NUM_SDCCH_PER_BTS); /* Send Channel request for emegergency call */ f_ipa_tx(0, ts_RSL_CHAN_RQD('A5'O, 23)); /* Expect the BSC to release one (the first) TCH/F on the BTS */ chan_nr := valueof(t_RslChanNr_Bm(1)); f_expect_chan_rel(0, chan_nr, expect_rr_chan_rel := false, expect_rll_rel_req := false); /* Expect the BSC to send activate/assign the a channel for the emergency call */ rx_rsl := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); chan_nr := rx_rsl.ies[0].body.chan_nr; f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(chan_nr, 33)); rx_rsl := f_exp_ipa_rx(0, tr_RSL_IMM_ASSIGN(0)); f_shutdown_helper(); } /* Hopping parameters per a timeslot */ private type record length(0..64) of GsmArfcn ArfcnList; private type record FHParamsTs { boolean enabled, uint6_t hsn, uint6_t maio, ArfcnList ma }; /* Hopping parameters per a transceiver */ private type record FHParamsTrx { GsmArfcn arfcn, FHParamsTs ts[8] }; /* Randomly generate the hopping parameters for the given timeslot numbers */ private function f_TC_fh_params_gen(template integer tr_tn := (1, 3, 5)) runs on test_CT return FHParamsTrx { var FHParamsTrx fhp; /* Generate a random ARFCN, including ARFCN 0 */ fhp.arfcn := f_rnd_int(3); for (var integer tn := 0; tn < 8; tn := tn + 1) { if (not match(tn, tr_tn)) { fhp.ts[tn].enabled := false; fhp.ts[tn].ma := { }; continue; } /* Random HSN / MAIO values: 0..63 */ fhp.ts[tn].hsn := f_rnd_int(64); fhp.ts[tn].maio := f_rnd_int(64); fhp.ts[tn].ma := { }; /* Random Mobile Allocation (hopping channels) */ var integer ma_len := 2 + f_rnd_int(9); /* 2..10 channels */ var integer step := 3 + f_rnd_int(4); /* 3..6 stepping */ for (var integer i := 1; i <= ma_len; i := i + 1) { fhp.ts[tn].ma := fhp.ts[tn].ma & { i * step }; } fhp.ts[tn].enabled := true; } log("f_TC_fh_params_gen(): ", fhp); return fhp; } /* Make sure that the given Channel Description IE matches the hopping configuration */ private function f_TC_fh_params_match_chan_desc(in FHParamsTrx fhp, in ChannelDescription cd) { var template (present) ChannelDescription tr_cd; var template (present) MaioHsn tr_maio_hsn; var uint3_t tn := cd.chan_nr.tn; if (fhp.ts[tn].enabled) { tr_maio_hsn := tr_HsnMaio(fhp.ts[tn].hsn, fhp.ts[tn].maio); tr_cd := tr_ChanDescH1(cd.chan_nr, tr_maio_hsn); } else { tr_cd := tr_ChanDescH0(cd.chan_nr, fhp.arfcn); } if (not match(cd, tr_cd)) { setverdict(fail, "Channel Description IE does not match: ", cd, " vs expected ", tr_cd); } } /* Make sure that the given Mobile Allocation IE matches the hopping configuration */ private function f_TC_fh_params_match_ma(in FHParamsTrx fhp, uint3_t tn, in MobileAllocationLV ma) { var template MobileAllocationLV tr_ma := f_TC_fh_params_gen_tr_ma(fhp, tn, ma); if (not match(ma, tr_ma)) { setverdict(fail, "Mobile Allocation IE does not match (tn := ", tn, "): ", ma, " vs expected: ", tr_ma); } else { setverdict(pass); } } private function f_TC_fh_params_gen_tr_ma(in FHParamsTrx fhp, uint3_t tn, in MobileAllocationLV ma) return template MobileAllocationLV { /* Mobile Allocation IE is expected to be empty if hopping is not enabled */ if (not fhp.ts[tn].enabled) { return { len := 0, ma := ''B }; } var bitstring full_mask := f_pad_bit(''B, 1024, '0'B); var bitstring slot_mask := f_pad_bit(''B, 1024, '0'B); var bitstring ma_mask := ''B; /* Compose the full bit-mask (all channels, up to 1024 entries) */ for (var integer i := 0; i < lengthof(fhp.ts); i := i + 1) { for (var integer j := 0; j < lengthof(fhp.ts[i].ma); j := j + 1) { if (full_mask[fhp.ts[i].ma[j]] == '1'B) { continue; } full_mask[fhp.ts[i].ma[j]] := '1'B; } } /* Take ARFCN of the TRX itself into account */ full_mask[fhp.arfcn] := '1'B; /* Compose a bit-mask for the given timeslot number */ for (var integer i := 0; i < lengthof(fhp.ts[tn].ma); i := i + 1) { slot_mask[fhp.ts[tn].ma[i]] := '1'B; } /* Finally, compose the Mobile Allocation bit-mask */ for (var integer i := 1; i < lengthof(full_mask); i := i + 1) { if (full_mask[i] != '1'B) { continue; } /* FIXME: ma_mask := ma_mask & slot_mask[i]; // triggers a bug in TITAN */ if (slot_mask[i] == '1'B) { ma_mask := ma_mask & '1'B; } else { ma_mask := ma_mask & '0'B; } } /* ARFCN 0 (if present) goes to the last position of the bit-mask */ if (full_mask[0] == '1'B) { /* FIXME: ma_mask := ma_mask & slot_mask[0]; // triggers a bug in TITAN */ if (slot_mask[0] == '1'B) { ma_mask := ma_mask & '1'B; } else { ma_mask := ma_mask & '0'B; } } /* Ensure that ma_mask is octet-aligned */ var integer ma_mask_len := (lengthof(ma_mask) + 8 - 1) / 8; ma_mask := f_pad_bit(ma_mask, ma_mask_len * 8, '0'B); return { len := ma_mask_len, ma := ma_mask }; } /* Configure the hopping parameters in accordance with the given record */ private function f_TC_fh_params_set(in FHParamsTrx fhp, uint8_t bts_nr := 0, uint8_t trx_nr := 0) runs on test_CT { /* Enter the configuration node for the given BTS/TRX numbers */ f_vty_enter_cfg_trx(BSCVTY, bts_nr, trx_nr); f_vty_transceive(BSCVTY, "arfcn " & int2str(fhp.arfcn)); for (var integer tn := 0; tn < lengthof(fhp.ts); tn := tn + 1) { f_vty_transceive(BSCVTY, "timeslot " & int2str(tn)); if (not fhp.ts[tn].enabled) { f_vty_transceive(BSCVTY, "hopping enabled 0"); f_vty_transceive(BSCVTY, "exit"); /* go back */ continue; } /* Configure HSN / MAIO values */ f_vty_transceive(BSCVTY, "hopping sequence-number " & int2str(fhp.ts[tn].hsn)); f_vty_transceive(BSCVTY, "hopping maio " & int2str(fhp.ts[tn].maio)); /* Configure the Mobile Allocation (hopping channels) */ for (var integer i := 0; i < lengthof(fhp.ts[tn].ma); i := i + 1) { f_vty_transceive(BSCVTY, "hopping arfcn add " & int2str(fhp.ts[tn].ma[i])); } f_vty_transceive(BSCVTY, "hopping enabled 1"); f_vty_transceive(BSCVTY, "exit"); /* go back */ } f_vty_transceive(BSCVTY, "end"); } /* Disable frequency hopping on all timeslots */ private function f_TC_fh_params_unset(in FHParamsTrx fhp, uint8_t bts_nr := 0, uint8_t trx_nr := 0, GsmArfcn arfcn := 871) runs on test_CT { /* Enter the configuration node for the given BTS/TRX numbers */ f_vty_enter_cfg_trx(BSCVTY, bts_nr, trx_nr); f_vty_transceive(BSCVTY, "arfcn " & int2str(arfcn)); for (var integer tn := 0; tn < lengthof(fhp.ts); tn := tn + 1) { f_vty_transceive(BSCVTY, "timeslot " & int2str(tn)); /* Delete all ARFCNs from the Mobile Allocation (if any) */ for (var integer i := 0; i < lengthof(fhp.ts[tn].ma); i := i + 1) { f_vty_transceive(BSCVTY, "hopping arfcn del " & int2str(fhp.ts[tn].ma[i])); } f_vty_transceive(BSCVTY, "hopping enabled 0"); f_vty_transceive(BSCVTY, "exit"); /* go back */ } f_vty_transceive(BSCVTY, "end"); f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); } /* Verify presence and correctness of the hopping parameters (HSN, MAIO) * in the Channel Identification IE of the RSL CHANnel ACTIVation message. */ testcase TC_fh_params_chan_activ() runs on test_CT { var FHParamsTrx fhp := f_TC_fh_params_gen(); var RSL_Message rsl_msg; var RSL_IE_Body ie; f_init_vty(); f_TC_fh_params_set(fhp); /* Enable frequency hopping */ f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1); /* CS domain: 3 (SDCCH/4+CBCH) + 4 (TCH/F) + 2 (TCH/H) channels available */ for (var integer i := 0; i < 9; i := i + 1) { f_ipa_tx(0, ts_RSL_CHAN_RQD(f_rnd_ra_cs(), 23)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); /* Make sure that Channel Identification IE is present */ if (not f_rsl_find_ie(rsl_msg, RSL_IE_CHAN_IDENT, ie)) { setverdict(fail, "RSL Channel Identification IE is absent"); continue; } /* Make sure that hopping parameters (HSN/MAIO) match */ f_TC_fh_params_match_chan_desc(fhp, ie.chan_ident.ch_desc.v); /* "Mobile Allocation shall be included but empty" - let's check this */ if (ie.chan_ident.ma.v.len != 0) { setverdict(fail, "Mobile Allocation IE is not empty: ", ie.chan_ident.ma, ", despite it shall be"); continue; } } /* Disable frequency hopping */ f_TC_fh_params_unset(fhp); f_shutdown_helper(); } /* Verify the hopping parameters (HSN, MAIO, MA) in (RR) Immediate Assignment */ testcase TC_fh_params_imm_ass() runs on test_CT { var FHParamsTrx fhp := f_TC_fh_params_gen(); var RSL_Message rsl_msg; var RSL_IE_Body ie; f_init_vty(); f_TC_fh_params_set(fhp); /* Enable frequency hopping */ f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1); /* CS domain: 3 (SDCCH/4+CBCH) + 4 (TCH/F) + 2 (TCH/H) channels available */ for (var integer i := 0; i < 9; i := i + 1) { f_ipa_tx(0, ts_RSL_CHAN_RQD(f_rnd_ra_cs(), 23)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(rsl_msg.ies[0].body.chan_nr, 33)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeC(RSL_MT_IMMEDIATE_ASSIGN_CMD)); /* Make sure that Full Immediate Assign Info IE is present */ if (not f_rsl_find_ie(rsl_msg, RSL_IE_FULL_IMM_ASS_INFO, ie)) { setverdict(fail, "RSL Full Immediate Assign Info IE is absent"); continue; } /* Decode the actual Immediate Assignment message */ var GsmRrMessage rr_msg := dec_GsmRrMessage(ie.full_imm_ass_info.payload); if (not match(rr_msg.header, t_RrHeader(IMMEDIATE_ASSIGNMENT, ?))) { setverdict(fail, "Failed to match Immediate Assignment: ", rr_msg); continue; } /* Make sure that hopping parameters (HSN/MAIO) match */ f_TC_fh_params_match_chan_desc(fhp, rr_msg.payload.imm_ass.chan_desc); /* Make sure that the Mobile Allocation IE matches */ f_TC_fh_params_match_ma(fhp, rr_msg.payload.imm_ass.chan_desc.chan_nr.tn, rr_msg.payload.imm_ass.mobile_allocation); } /* Disable frequency hopping */ f_TC_fh_params_unset(fhp); f_shutdown_helper(); } /* Verify the hopping parameters (HSN, MAIO, MA) in (RR) Assignment Command */ testcase TC_fh_params_assignment_cmd() runs on test_CT { var FHParamsTrx fhp := f_TC_fh_params_gen(); var RSL_Message rsl_msg; var RSL_IE_Body ie; f_init_vty(); f_TC_fh_params_set(fhp); /* Enable frequency hopping */ f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1); /* HACK: work around "Couldn't find Expect for CRCX" */ vc_MGCP.stop; var template PDU_BSSAP ass_cmd := f_gen_ass_req(); ass_cmd.pdu.bssmap.assignmentRequest.codecList := ts_BSSMAP_IE_CodecList({ts_CodecFR}); /* CS domain (TCH): 4 (TCH/F) + 2 (TCH/H) channels available * NOTE: only 3 SDCCH/4 channels are available on CCCH+SDCCH4+CBCH */ for (var integer i := 0; i < 3; i := i + 1) { /* Establish a dedicated channel, so we can trigger (late) TCH assignment */ var DchanTuple dt := f_est_dchan(f_rnd_ra_cs(), 23, f_rnd_octstring(16)); /* Send a BSSMAP Assignment Command, expect CHANnel ACTIVation */ BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ass_cmd)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); /* ACKnowledge CHANnel ACTIVation, expect RSL DATA REQuest */ f_ipa_tx(0, ts_RSL_CHAN_ACT_ACK(rsl_msg.ies[0].body.chan_nr, 33)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeR(RSL_MT_DATA_REQ)); /* Make sure that L3 Information IE is present */ if (not f_rsl_find_ie(rsl_msg, RSL_IE_L3_INFO, ie)) { setverdict(fail, "RSL L3 Information IE is absent"); continue; } /* Decode the L3 message and make sure it is (RR) Assignment Command */ var GsmRrL3Message l3_msg := dec_GsmRrL3Message(ie.l3_info.payload); if (not match(l3_msg.header, t_RrL3Header(ASSIGNMENT_COMMAND))) { setverdict(fail, "Failed to match Assignment Command: ", l3_msg); continue; } /* Make sure that hopping parameters (HSN/MAIO) match */ var ChannelDescription chan_desc := l3_msg.payload.ass_cmd.chan_desc; f_TC_fh_params_match_chan_desc(fhp, chan_desc); /* Make sure that Cell Channel Description IE is present if FH is enabled */ if (chan_desc.h and not ispresent(l3_msg.payload.ass_cmd.cell_chan_desc)) { setverdict(fail, "FH enabled, but Cell Channel Description IE is absent"); continue; } /* Make sure that the Mobile Allocation IE matches (if present) */ var boolean ma_present := ispresent(l3_msg.payload.ass_cmd.mobile_allocation); if (chan_desc.h and ma_present) { f_TC_fh_params_match_ma(fhp, chan_desc.chan_nr.tn, l3_msg.payload.ass_cmd.mobile_allocation.v); } else if (chan_desc.h and not ma_present) { setverdict(fail, "FH enabled, but Mobile Allocation IE is absent"); continue; } else if (not chan_desc.h and ma_present) { setverdict(fail, "FH disabled, but Mobile Allocation IE is present"); continue; } } /* Give the IUT some time to release all channels */ f_sleep(3.0); /* Disable frequency hopping */ f_TC_fh_params_unset(fhp); f_shutdown_helper(); } /* Verify the hopping parameters (HSN, MAIO, MA) in (RR) Handover Command */ private function f_TC_fh_params_handover_cmd(in FHParamsTrx fhp) runs on test_CT { var RSL_Message rsl_msg; var RSL_IE_Body ie; var DchanTuple dt; /* Establish a dedicated channel, so we can trigger handover */ dt := f_est_dchan(f_rnd_ra_cs(), 23, f_rnd_octstring(16)); /* Trigger handover from BTS0 to BTS1 */ f_bts_0_cfg(BSCVTY, { "neighbor bts 1" }); f_vty_handover(BSCVTY, 0, 0, dt.rsl_chan_nr, 1); /* Expect RSL CHANnel ACTIVation on BTS1/TRX0/TS1 */ rsl_msg := f_exp_ipa_rx(1, tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV)); /* ACKnowledge channel activation and expect (RR) Handover Command */ f_ipa_tx(1, ts_RSL_CHAN_ACT_ACK(rsl_msg.ies[0].body.chan_nr, 33)); rsl_msg := f_exp_ipa_rx(0, tr_RSL_MsgTypeR(RSL_MT_DATA_REQ)); /* Make sure that L3 Information IE is present */ if (not f_rsl_find_ie(rsl_msg, RSL_IE_L3_INFO, ie)) { setverdict(fail, "RSL L3 Information IE is absent"); return; } /* Decode the L3 message and make sure it is (RR) Handover Command */ var GsmRrL3Message l3_msg := dec_GsmRrL3Message(ie.l3_info.payload); if (not match(l3_msg.header, t_RrL3Header(HANDOVER_COMMAND))) { setverdict(fail, "Failed to match Handover Command: ", l3_msg); return; } /* Make sure that we've got SDCCH/8 on TS1 (expected to be hopping) */ var ChannelDescription chan_desc := l3_msg.payload.ho_cmd.chan_desc; if (not match(chan_desc.chan_nr, t_RslChanNr_SDCCH8(1, ?))) { setverdict(fail, "Unexpected channel number: ", chan_desc.chan_nr); return; } /* Make sure that hopping parameters (HSN/MAIO) match */ f_TC_fh_params_match_chan_desc(fhp, chan_desc); /* Make sure that Cell Channel Description IE is present */ if (not ispresent(l3_msg.payload.ho_cmd.cell_chan_desc)) { setverdict(fail, "FH enabled, but Cell Channel Description IE is absent"); return; } /* Make sure that the Mobile Allocation (after time) IE is present and matches */ var boolean ma_present := ispresent(l3_msg.payload.ho_cmd.mobile_allocation); if (ma_present) { f_TC_fh_params_match_ma(fhp, chan_desc.chan_nr.tn, l3_msg.payload.ho_cmd.mobile_allocation.v); } else { setverdict(fail, "FH enabled, but Mobile Allocation IE is absent"); return; } } testcase TC_fh_params_handover_cmd() runs on test_CT { var FHParamsTrx fhp := f_TC_fh_params_gen(); f_init_vty(); /* (Re)configure TS0 as BCCH and TS1 as SDCCH8 on BTS1/TRX0 */ f_vty_enter_cfg_trx(BSCVTY, bts := 1, trx := 0); f_vty_transceive(BSCVTY, "timeslot 0"); f_vty_transceive(BSCVTY, "phys_chan_config ccch"); f_vty_transceive(BSCVTY, "exit"); /* go back */ f_vty_transceive(BSCVTY, "timeslot 1"); f_vty_transceive(BSCVTY, "phys_chan_config sdcch8"); f_vty_transceive(BSCVTY, "end"); /* we're done */ f_TC_fh_params_set(fhp, 1); /* Enable frequency hopping on BTS1 */ f_vty_transceive(BSCVTY, "drop bts connection 1 oml"); f_init(2); f_TC_fh_params_handover_cmd(fhp); /* Disable frequency hopping on BTS1 */ f_TC_fh_params_unset(fhp, 1); /* (Re)configure TS0 as CCCH+SDCCH4+CBCH and TS1 as TCH/F */ f_vty_enter_cfg_trx(BSCVTY, bts := 1, trx := 0); f_vty_transceive(BSCVTY, "timeslot 0"); f_vty_transceive(BSCVTY, "phys_chan_config ccch+sdcch4+cbch"); f_vty_transceive(BSCVTY, "exit"); /* go back */ f_vty_transceive(BSCVTY, "timeslot 1"); f_vty_transceive(BSCVTY, "phys_chan_config tch/f"); f_vty_transceive(BSCVTY, "end"); /* we're done */ f_shutdown_helper(); } /* Verify the hopping parameters in System Information Type 4 */ testcase TC_fh_params_si4_cbch() runs on test_CT { var FHParamsTrx fhp := f_TC_fh_params_gen(tr_tn := 1); var ASP_RSL_Unitdata rx_rsl_ud; timer T := 5.0; f_init_vty(); /* (Re)configure TS0 as BCCH and TS1 as SDCCH8+CBCH */ f_vty_enter_cfg_trx(BSCVTY, trx := 0); f_vty_transceive(BSCVTY, "timeslot 0"); f_vty_transceive(BSCVTY, "phys_chan_config ccch"); f_vty_transceive(BSCVTY, "exit"); /* go back */ f_vty_transceive(BSCVTY, "timeslot 1"); f_vty_transceive(BSCVTY, "phys_chan_config sdcch8+cbch"); f_vty_transceive(BSCVTY, "end"); /* we're done */ f_TC_fh_params_set(fhp); /* Enable frequency hopping */ f_vty_transceive(BSCVTY, "drop bts connection 0 oml"); f_init(1); T.start; alt { [] IPA_RSL[0].receive(tr_ASP_RSL_UD(tr_RSL_BCCH_INFO(RSL_SYSTEM_INFO_4))) -> value rx_rsl_ud { var RSL_IE_Body ie := rx_rsl_ud.rsl.ies[2].body; /* FULL BCCH Information IE */ var SystemInformation si := dec_SystemInformation(ie.other.payload); /* Make sure that what we decoded is System Information Type 4 */ if (si.header.message_type != SYSTEM_INFORMATION_TYPE_4) { setverdict(fail, "RSL FULL BCCH Information IE contains: ", si); repeat; } /* Make sure that CBCH Channel Description IE is present */ if (not ispresent(si.payload.si4.cbch_chan_desc)) { setverdict(fail, "CBCH Channel Description IE is absent"); break; } /* Finally, check the hopping parameters (HSN, MAIO) */ var ChannelDescription chan_desc := si.payload.si4.cbch_chan_desc.v; f_TC_fh_params_match_chan_desc(fhp, chan_desc); /* 3GPP TS 44.018, section 9.1.36.2 "CBCH Mobile Allocation": * The CBCH Mobile Allocation IE *shall* be present if FH is enabled. */ if (chan_desc.h and not ispresent(si.payload.si4.cbch_mobile_alloc)) { setverdict(fail, "FH enabled, but Mobile Allocation IE is absent"); break; } else if (chan_desc.h and ispresent(si.payload.si4.cbch_mobile_alloc)) { f_TC_fh_params_match_ma(fhp, chan_desc.chan_nr.tn, si.payload.si4.cbch_mobile_alloc.v); } } [] IPA_RSL[0].receive { repeat; } [] T.timeout { setverdict(fail, "Timeout waiting for RSL BCCH INFOrmation (SI4)"); } } /* Disable frequency hopping */ f_TC_fh_params_unset(fhp); /* (Re)configure TS0 as CCCH+SDCCH4+CBCH and TS1 as TCH/F */ f_vty_enter_cfg_trx(BSCVTY, trx := 0); f_vty_transceive(BSCVTY, "timeslot 0"); f_vty_transceive(BSCVTY, "phys_chan_config ccch+sdcch4+cbch"); f_vty_transceive(BSCVTY, "exit"); /* go back */ f_vty_transceive(BSCVTY, "timeslot 1"); f_vty_transceive(BSCVTY, "phys_chan_config tch/f"); f_vty_transceive(BSCVTY, "end"); /* we're done */ f_shutdown_helper(); } template (value) PDU_BSSAP_LE ts_BSSMAP_LE_BSSLAP(template (value) BSSLAP_PDU bsslap) := ts_BSSMAP_LE_ConnInfo(BSSMAP_LE_PROT_BSSLAP, data := enc_BSSLAP_PDU(valueof(bsslap))); private function f_match_bsslap(PDU_BSSAP_LE got_bsslap_msg, template (present) BSSLAP_PDU expect_bsslap) { var BSSLAP_PDU bsslap := dec_BSSLAP_PDU(got_bsslap_msg.pdu.bssmap.co_info.bsslap_apdu.data); if (not match(bsslap, expect_bsslap)) { log("EXPECTING BSSLAP: ", expect_bsslap); log("GOT BSSLAP: ", bsslap); setverdict(fail, "BSSLAP is not as expected"); mtc.stop; } setverdict(pass); } /* GAD: this is an Ellipsoid point with uncertainty circle, encoded as in 3GPP TS 23.032 ยง7.3.2. */ const octetstring gad_ell_point_unc_circle := '10b0646d0d5f6627'O; private function f_expect_bsslap(template (present) BSSLAP_PDU expect_rx_bsslap) runs on MSC_ConnHdlr { var PDU_BSSAP_LE rx_bsslap; BSSAP_LE.receive(tr_BSSMAP_LE_ConnInfo(BSSMAP_LE_PROT_BSSLAP, ?)) -> value(rx_bsslap); f_match_bsslap(rx_bsslap, expect_rx_bsslap); } /* With an active lchan, start BSSMAP Perform Location Request on A interface, starting BSSMAP-LE Perform Location * Request on Lb interface. Either with or without the SMLC doing a BSSLAP TA Request. */ private function f_lcs_loc_req_for_active_ms(boolean do_ta_request := false) runs on MSC_ConnHdlr { f_sleep(1.0); f_establish_fully(omit, omit); f_bssap_le_register_imsi(g_pars.imsi, omit); BSSAP.send(valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('262'H, '42'H, 23, 42)))); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); if (not do_ta_request) { /* verify TA Layer 3 in APDU. First the APDU type (BSSLAP), then the BSSLAP data contents. */ var template BSSMAP_LE_IE_APDU expect_apdu := tr_BSSMAP_LE_APDU(BSSMAP_LE_PROT_BSSLAP, ?); if (not match(plr.pdu.bssmap.perf_loc_req.bsslap_apdu, expect_apdu)) { log("EXPECTING BSSMAP-LE APDU IE ", expect_apdu); log("GOT BSSMAP-LE APDU IE ", plr.pdu.bssmap.perf_loc_req.bsslap_apdu); setverdict(fail, "BSSMAP-LE APDU IE is not as expected"); mtc.stop; } var template BSSLAP_PDU expect_ta_layer3 := tr_BSSLAP_TA_Layer3(tr_BSSLAP_IE_TA(0)); var BSSLAP_PDU bsslap := dec_BSSLAP_PDU(plr.pdu.bssmap.perf_loc_req.bsslap_apdu.data); if (not match(bsslap, expect_ta_layer3)) { log("EXPECTING BSSLAP TA Layer 3: ", expect_ta_layer3); log("GOT BSSLAP: ", bsslap); setverdict(fail, "BSSLAP is not as expected"); mtc.stop; } /* OsmoBSC directly sent the TA as BSSLAP APDU in the BSSMAP-LE Perform Location Request to the SMLC. The SMLC * has no need to request the TA from the BSC and directly responds. */ } else { /* SMLC wants to ask the TA from the BSC explicitly in a BSSLAP TA Request message */ BSSAP_LE.send(ts_BSSMAP_LE_BSSLAP(ts_BSSLAP_TA_Req)); f_expect_bsslap(tr_BSSLAP_TA_Resp(?, ?)); } /* SMLC got the TA from the BSC, now responds with geo information data. */ BSSAP_LE.send(ts_BSSMAP_LE_PerfLocResp(gad_ell_point_unc_circle, omit)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); BSSAP.receive(tr_BSSMAP_Perform_Location_Response(tr_BSSMAP_IE_LocationEstimate(gad_ell_point_unc_circle))); /* The LCS was using an active A-interface conn. It should still remain active after this. */ f_mo_l3_transceive(); f_perform_clear(RSL); f_sleep(2.0); setverdict(pass); } /* With an active lchan, start BSSMAP Perform Location Request on A interface, starting BSSMAP-LE Perform Location * Request on Lb interface. Without the SMLC doing a BSSLAP TA Request. */ private function f_tc_lcs_loc_req_for_active_ms(charstring id) runs on MSC_ConnHdlr { f_lcs_loc_req_for_active_ms(false); } testcase TC_lcs_loc_req_for_active_ms() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_active_ms), pars); vc_conn.done; } /* With an active lchan, start BSSMAP Perform Location Request on A interface, starting BSSMAP-LE Perform Location * Request on Lb interface. With the SMLC doing a BSSLAP TA Request. */ private function f_tc_lcs_loc_req_for_active_ms_ta_req(charstring id) runs on MSC_ConnHdlr { f_lcs_loc_req_for_active_ms(true); } testcase TC_lcs_loc_req_for_active_ms_ta_req() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_active_ms_ta_req), pars); vc_conn.done; } /* Clear the A-interface conn only, without doing anything on Abis. Useful for LCS, for cases where there is only an A * conn without an active lchan. */ private function f_clear_A_conn() runs on MSC_ConnHdlr { var BssmapCause cause := 0; BSSAP.send(ts_BSSMAP_ClearCommand(cause)); BSSAP.receive(tr_BSSMAP_ClearComplete); BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ); timer no_more_bssap := 5.0; no_more_bssap.start; alt { [] no_more_bssap.timeout { break; } [] BSSAP.receive(tr_BSSAP_BSSMAP) { setverdict(fail, "Expected no more BSSAP after Clear Complete"); mtc.stop; } } setverdict(pass); } /* Verify that the A-interface connection is still working, and then clear it, without doing anything on Abis. Useful * for LCS, for cases where there is only an A conn without an active lchan. */ private function f_verify_active_A_conn_and_clear() runs on MSC_ConnHdlr { f_logp(BSCVTY, "f_verify_active_A_conn_and_clear: test A link, then clear"); /* When an lchan is active, we can send some L3 data from the BTS side and verify that it shows up on the other * side towards the MSC. When there is no lchan, this is not possible. To probe whether the A-interface * connection is still up, we need something that echos back on the A-interface. Another LCS request! */ BSSAP.send(valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('262'H, '42'H, 23, 42)))); BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)); /* Right, the Perform Location Request showed up on Lb, now we can clear the A conn. */ f_clear_A_conn(); BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocAbort(BSSMAP_LE_LCS_CAUSE_REQUEST_ABORTED)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); } /* With *no* active lchan, start BSSMAP Perform Location Request on A interface, starting BSSMAP-LE Perform Location * Request on Lb interface. BSC will Page for the subscriber as soon as we (virtual SMLC) request the TA via BSSLAP. */ private function f_tc_lcs_loc_req_for_idle_ms(charstring id) runs on MSC_ConnHdlr { f_sleep(1.0); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); f_bssap_le_register_imsi(g_pars.imsi, omit); /* Register to receive the Paging Command */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); g_chan_nr := new_chan_nr; f_rslem_register(0, g_chan_nr); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('001'H, '01'H, 1, 0))))); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); /* SMLC wants to ask the TA from the BSC explicitly in a BSSLAP TA Request message */ BSSAP_LE.send(ts_BSSMAP_LE_BSSLAP(ts_BSSLAP_TA_Req)); /* OsmoBSC needs to Page */ RSL.receive(tr_RSL_PAGING_CMD(tr_MI_IMSI(g_pars.imsi))); f_logp(BSCVTY, "got Paging Command"); /* MS requests channel. Since the Paging was for LCS, the Paging Response does not trigger a Complete Layer 3 to * the MSC, and releases the lchan directly. */ f_perform_compl_l3(RSL, ts_PAG_RESP(valueof(ts_MI_IMSI_LV(g_pars.imsi))), do_clear := false, expect_bssmap_l3 := false); f_expect_lchan_rel(RSL); /* From the Paging Response, the TA is now known to the BSC, and it responds to the SMLC. */ f_expect_bsslap(tr_BSSLAP_TA_Resp(?, ?)); /* SMLC got the TA from the BSC, now responds with geo information data. */ BSSAP_LE.send(ts_BSSMAP_LE_PerfLocResp(gad_ell_point_unc_circle, omit)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); BSSAP.receive(tr_BSSMAP_Perform_Location_Response(tr_BSSMAP_IE_LocationEstimate(gad_ell_point_unc_circle))); /* The lchan is gone, the A-interface conn was created for the LCS only. * Still it is clearly the MSC's job to decide whether to tear down the conn or not. */ f_verify_active_A_conn_and_clear(); f_sleep(2.0); setverdict(pass); } testcase TC_lcs_loc_req_for_idle_ms() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_idle_ms), pars); vc_conn.done; } /* With no active lchan, start BSSMAP Perform Location Request on A interface, but omit IMSI; expect failure response. */ private function f_tc_lcs_loc_req_no_subscriber(charstring id) runs on MSC_ConnHdlr { f_sleep(1.0); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); f_bssap_le_register_imsi(g_pars.imsi, omit); /* provoke an abort by omitting both IMSI and IMEI */ BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Perform_Location_Request(omit, ts_CellId_CGI('262'H, '42'H, 23, 42))))); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); /* BSC tells MSC about failure */ BSSAP.receive(tr_BSSMAP_Perform_Location_Response( locationEstimate := omit, positioningData := omit, lCS_Cause := tr_BSSMAP_LcsCause(BSSMAP_LCS_CAUSE_DATA_MISSING_IN_REQ))); /* There is no lchan. Still the MSC's job to decide whether to tear down the conn or not. */ f_verify_active_A_conn_and_clear(); f_sleep(2.0); setverdict(pass); } testcase TC_lcs_loc_req_no_subscriber() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_no_subscriber), pars); vc_conn.done; } /* With an active lchan, start a Perform Location Request on the A-interface, but virtual SMLC does not answer with * BSSMAP-LE Perform Location Response (before or after sending a BSSLAP TA Request) */ private function f_lcs_loc_req_for_active_ms_le_timeout(boolean do_ta) runs on MSC_ConnHdlr { f_sleep(1.0); f_establish_fully(omit, omit); f_bssap_le_register_imsi(g_pars.imsi, omit); BSSAP.send(valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('262'H, '42'H, 23, 42)))); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); if (do_ta) { /* SMLC wants to ask the TA from the BSC explicitly in a BSSLAP TA Request message */ BSSAP_LE.send(ts_BSSMAP_LE_BSSLAP(ts_BSSLAP_TA_Req)); f_expect_bsslap(tr_BSSLAP_TA_Resp(?, ?)); } /* SMLC fails to respond, BSC runs into timeout */ BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocAbort(BSSMAP_LE_LCS_CAUSE_SYSTEM_FAILURE)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); BSSAP.receive(tr_BSSMAP_Perform_Location_Response( locationEstimate := omit, positioningData := omit, lCS_Cause := tr_BSSMAP_LcsCause(BSSMAP_LCS_CAUSE_SYSTEM_FAILURE))); /* There is no lchan. Still the MSC's job to decide whether to tear down the conn or not. */ f_verify_active_A_conn_and_clear(); f_sleep(2.0); setverdict(pass); } /* With an active lchan, start a Perform Location Request on the A-interface, but virtual SMLC does not answer with * BSSMAP-LE Perform Location Response, without sending a BSSLAP TA Request. */ private function f_tc_lcs_loc_req_for_active_ms_le_timeout(charstring id) runs on MSC_ConnHdlr { f_lcs_loc_req_for_active_ms_le_timeout(false); } testcase TC_lcs_loc_req_for_active_ms_le_timeout() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_active_ms_le_timeout), pars); vc_conn.done; } /* With an active lchan, start a Perform Location Request on the A-interface, but virtual SMLC does not answer with * BSSMAP-LE Perform Location Response, after sending a BSSLAP TA Request. */ private function f_tc_lcs_loc_req_for_active_ms_le_timeout2(charstring id) runs on MSC_ConnHdlr { f_lcs_loc_req_for_active_ms_le_timeout(true); } testcase TC_lcs_loc_req_for_active_ms_le_timeout2() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_active_ms_le_timeout2), pars); vc_conn.done; } /* With *no* active lchan, start a Perform Location Request, expecting that the MS will be Paged. */ private function f_tc_lcs_loc_req_for_idle_ms_no_pag_resp(charstring id) runs on MSC_ConnHdlr { f_sleep(1.0); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); f_bssap_le_register_imsi(g_pars.imsi, omit); /* Register to receive the Paging Command */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); g_chan_nr := new_chan_nr; f_rslem_register(0, g_chan_nr); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('001'H, '01'H, 1, 0))))); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); /* SMLC wants to ask the TA from the BSC explicitly in a BSSLAP TA Request message */ BSSAP_LE.send(ts_BSSMAP_LE_BSSLAP(ts_BSSLAP_TA_Req)); /* OsmoBSC needs to Page */ var PDU_BSSAP_LE rx_bsslap; alt { [] RSL.receive(tr_RSL_PAGING_CMD(tr_MI_IMSI(g_pars.imsi))) { f_logp(BSCVTY, "got Paging Command"); repeat; } [] BSSAP_LE.receive(tr_BSSMAP_LE_ConnInfo(BSSMAP_LE_PROT_BSSLAP, ?)) -> value(rx_bsslap) { /* MS does not respond to Paging, TA Req runs into timeout. */ f_match_bsslap(rx_bsslap, tr_BSSLAP_Abort(?)); } } /* SMLC responds with failure */ BSSAP_LE.send(ts_BSSMAP_LE_PerfLocResp(omit, BSSMAP_LE_LCS_CAUSE_REQUEST_ABORTED)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); /* BSC tells MSC about failure */ BSSAP.receive(tr_BSSMAP_Perform_Location_Response( locationEstimate := omit, positioningData := omit, lCS_Cause := tr_BSSMAP_LcsCause(BSSMAP_LCS_CAUSE_REQUEST_ABORTED))); /* There is no lchan. Still the MSC's job to decide whether to tear down the conn or not. */ f_verify_active_A_conn_and_clear(); f_sleep(2.0); setverdict(pass); } testcase TC_lcs_loc_req_for_idle_ms_no_pag_resp() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_lcs_loc_req_for_idle_ms_no_pag_resp), pars); vc_conn.done; } /* During an ongoing Location Request, the MS sends a CM Service Request. Expect the same A-conn to be re-used / taken * over. */ private function f_tc_cm_service_during_lcs_loc_req(charstring id) runs on MSC_ConnHdlr { f_sleep(1.0); f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3", FR_AMR); f_bssap_le_register_imsi(g_pars.imsi, omit); /* Register to receive the Paging Command */ var RslChannelNr new_chan_nr := valueof(t_RslChanNr0(1, RSL_CHAN_NR_Bm_ACCH)); g_chan_nr := new_chan_nr; f_rslem_register(0, g_chan_nr); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_bsc, g_pars.sccp_addr_msc, valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('001'H, '01'H, 1, 0))))); BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); /* As the A-interface conn was established for LCS, the MS coincidentally decides to issue a CM Service Request * and establish Layer 3. It should use the existing A-interface conn. */ f_perform_compl_l3(RSL, valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(ts_MI_IMSI_LV(g_pars.imsi)))), do_clear := false, expect_bssmap_l3 := true); /* SMLC wants to ask the TA from the BSC explicitly in a BSSLAP TA Request message */ BSSAP_LE.send(ts_BSSMAP_LE_BSSLAP(ts_BSSLAP_TA_Req)); /* OsmoBSC already has an lchan, no need to Page, just returns the TA */ f_expect_bsslap(tr_BSSLAP_TA_Resp(?, ?)); /* SMLC got the TA from the BSC, now responds with geo information data. */ BSSAP_LE.send(ts_BSSMAP_LE_PerfLocResp(gad_ell_point_unc_circle, omit)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); BSSAP.receive(tr_BSSMAP_Perform_Location_Response(tr_BSSMAP_IE_LocationEstimate(gad_ell_point_unc_circle))); /* The lchan should still exist, it was from a CM Service Request. */ f_mo_l3_transceive(); f_perform_clear(RSL); f_sleep(2.0); setverdict(pass); } testcase TC_cm_service_during_lcs_loc_req() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(1, true); f_sleep(1.0); pars.sccp_addr_msc := g_bssap[0].sccp_addr_own; pars.sccp_addr_bsc := g_bssap[0].sccp_addr_peer; vc_conn := f_start_handler(refers(f_tc_cm_service_during_lcs_loc_req), pars); vc_conn.done; } /* During an ongoing Perform Location Request, do a Handover, an expect a BSSLAP Reset message from the BSC to indicate * the new lchan after handover. */ private function f_tc_ho_during_lcs_loc_req(charstring id) runs on MSC_ConnHdlr { f_sleep(1.0); f_establish_fully(omit, omit); f_bssap_le_register_imsi(g_pars.imsi, omit); BSSAP.send(valueof(ts_BSSMAP_Perform_Location_Request(ts_BSSMAP_Imsi(g_pars.imsi), ts_CellId_CGI('262'H, '42'H, 23, 42)))); var PDU_BSSAP_LE plr; BSSAP_LE.receive(tr_BSSMAP_LE_PerfLocReq(BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC_LOC, ?, ?)) -> value(plr); /* SMLC ponders the Location Request, in the meantime the BSC decides to handover */ f_bts_0_cfg(BSCVTY, {"neighbor bts 1"}); var HandoverState hs := { rr_ho_cmpl_seen := false, handover_done := false, old_chan_nr := - }; /* issue hand-over command on VTY */ f_vty_handover(BSCVTY, 0, 0, g_chan_nr, 1); /* temporarily suspend DChan processing on BTS1 to avoid race with RSLEM_register */ f_rslem_suspend(RSL1_PROC); /* From the MGW perspective, a handover is is characterized by * performing one MDCX operation with the MGW. So we expect to see * one more MDCX during handover. */ g_media.mgcp_conn[0].mdcx_seen_exp := g_media.mgcp_conn[0].crcx_seen_exp + 1; alt { [] as_handover(hs); } var PDU_BSSAP_LE rx_bsslap; interleave { /* Expect the BSC to inform the MSC about the handover */ [] BSSAP.receive(tr_BSSMAP_HandoverPerformed); /* Expect the BSC to inform the SMLC about the handover */ [] BSSAP_LE.receive(tr_BSSMAP_LE_ConnInfo(BSSMAP_LE_PROT_BSSLAP, ?)) -> value(rx_bsslap) { f_match_bsslap(rx_bsslap, tr_BSSLAP_Reset(BSSLAP_CAUSE_INTRA_BSS_HO)); } } /* SMLC now responds with geo information data. */ BSSAP_LE.send(ts_BSSMAP_LE_PerfLocResp(gad_ell_point_unc_circle, omit)); BSSAP_LE.receive(BSSAP_LE_Conn_Prim:CONN_PRIM_DISC_IND); BSSAP.receive(tr_BSSMAP_Perform_Location_Response(tr_BSSMAP_IE_LocationEstimate(gad_ell_point_unc_circle))); /* lchan still active */ f_mo_l3_transceive(RSL1); /* MSC decides it is done now. */ f_perform_clear(RSL1); f_sleep(2.0); setverdict(pass); } testcase TC_ho_during_lcs_loc_req() runs on test_CT { var MSC_ConnHdlr vc_conn; var TestHdlrParams pars := f_gen_test_hdlr_pars(); f_init(2, true); f_sleep(1.0); vc_conn := f_start_handler(refers(f_tc_ho_during_lcs_loc_req), pars); vc_conn.done; } /* Dyn PDCH todo: * activate OSMO as TCH/F * activate OSMO as TCH/H * does the BSC-located PCU socket get the updated INFO? * what if no PCU is connected at the time? * is the info correct on delayed PCU (re)connect? */ control { /* CTRL interface testing */ execute( TC_ctrl_msc_connection_status() ); execute( TC_ctrl_msc0_connection_status() ); execute( TC_ctrl() ); if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_SCCPlite_SERVER) { execute( TC_ctrl_location() ); } execute( TC_si_default() ); execute( TC_si2quater_2_earfcns() ); execute( TC_si2quater_3_earfcns() ); execute( TC_si2quater_4_earfcns() ); execute( TC_si2quater_5_earfcns() ); execute( TC_si2quater_6_earfcns() ); execute( TC_si2quater_12_earfcns() ); execute( TC_si2quater_23_earfcns() ); execute( TC_si2quater_32_earfcns() ); execute( TC_si2quater_33_earfcns() ); execute( TC_si2quater_42_earfcns() ); execute( TC_si2quater_48_earfcns() ); execute( TC_si2quater_49_earfcns() ); execute( TC_si_acc_rotate() ); execute( TC_si_acc_ramp_rotate() ); /* RSL DCHAN Channel ACtivation / Deactivation */ execute( TC_chan_act_noreply() ); execute( TC_chan_act_counter() ); execute( TC_chan_act_ack_noest() ); execute( TC_chan_act_ack_noest_emerg() ); execute( TC_chan_rqd_emerg_deny() ); execute( TC_chan_act_ack_est_ind_noreply() ); execute( TC_chan_act_ack_est_ind_refused() ); execute( TC_chan_act_nack() ); execute( TC_chan_exhaustion() ); execute( TC_chan_deact_silence() ); execute( TC_chan_rel_rll_rel_ind() ); execute( TC_chan_rel_conn_fail() ); execute( TC_chan_rel_hard_clear() ); execute( TC_chan_rel_hard_clear_csfb() ); execute( TC_chan_rel_hard_rlsd() ); execute( TC_chan_rel_hard_rlsd_ms_dead() ); execute( TC_chan_rel_a_reset() ); execute( TC_chan_rel_sccp_tiar_timeout() ); execute( TC_chan_rel_rr_cause() ); execute( TC_outbound_connect() ); /* Assignment related */ execute( TC_assignment_cic_only() ); execute( TC_assignment_csd() ); execute( TC_assignment_ctm() ); execute( TC_assignment_sign() ); if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { execute( TC_assignment_aoip_tla_v6() ); } execute( TC_assignment_fr_a5_0() ); execute( TC_assignment_fr_a5_1() ); if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { execute( TC_assignment_fr_a5_1_codec_missing() ); } execute( TC_assignment_fr_a5_3() ); execute( TC_assignment_fr_a5_4() ); execute( TC_ciph_mode_a5_0() ); execute( TC_ciph_mode_a5_1() ); execute( TC_ciph_mode_a5_3() ); execute( TC_assignment_codec_fr() ); execute( TC_assignment_codec_hr() ); execute( TC_assignment_codec_efr() ); execute( TC_assignment_codec_amr_f() ); execute( TC_assignment_codec_amr_h() ); if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { execute( TC_assignment_codec_amr_f_S1() ); execute( TC_assignment_codec_amr_h_S1() ); execute( TC_assignment_codec_amr_f_S124() ); execute( TC_assignment_codec_amr_h_S124() ); execute( TC_assignment_codec_amr_f_S0() ); execute( TC_assignment_codec_amr_f_S02() ); execute( TC_assignment_codec_amr_f_S024() ); execute( TC_assignment_codec_amr_f_S0247() ); execute( TC_assignment_codec_amr_h_S0() ); execute( TC_assignment_codec_amr_h_S02() ); execute( TC_assignment_codec_amr_h_S024() ); execute( TC_assignment_codec_amr_h_S0247() ); execute( TC_assignment_codec_amr_f_S01234567() ); execute( TC_assignment_codec_amr_f_S0234567() ); execute( TC_assignment_codec_amr_f_zero() ); execute( TC_assignment_codec_amr_f_unsupp() ); execute( TC_assignment_codec_amr_h_S7() ); } execute( TC_assignment_codec_fr_exhausted_req_hr() ); execute( TC_assignment_codec_fr_exhausted_req_fr() ); execute( TC_assignment_codec_fr_exhausted_req_fr_hr() ); execute( TC_assignment_codec_fr_exhausted_req_hr_fr() ); execute( TC_assignment_codec_hr_exhausted_req_fr() ); execute( TC_assignment_codec_hr_exhausted_req_hr() ); execute( TC_assignment_codec_hr_exhausted_req_hr_fr() ); execute( TC_assignment_codec_hr_exhausted_req_fr_hr() ); execute( TC_assignment_codec_req_hr_fr() ); execute( TC_assignment_codec_req_fr_hr() ); if (mp_enable_osmux_test) { execute( TC_assignment_osmux() ); } /* RLL Establish Indication on inactive DCHAN / SAPI */ execute( TC_rll_est_ind_inact_lchan() ); execute( TC_rll_est_ind_inval_sapi1() ); execute( TC_rll_est_ind_inval_sapi3() ); execute( TC_rll_est_ind_inval_sacch() ); /* DLCI / RSL Link ID conversion for MO/MT messages on SAPI0/SAPI3 */ execute( TC_tch_dlci_link_id_sapi() ); /* SAPI N Reject triggered by RLL establishment failures */ execute( TC_rll_rel_ind_sapi_n_reject() ); execute( TC_rll_err_ind_sapi_n_reject() ); execute( TC_rll_timeout_sapi_n_reject() ); /* Paging related tests */ execute( TC_paging_imsi_nochan() ); execute( TC_paging_tmsi_nochan() ); execute( TC_paging_tmsi_any() ); execute( TC_paging_tmsi_sdcch() ); execute( TC_paging_tmsi_tch_f() ); execute( TC_paging_tmsi_tch_hf() ); execute( TC_paging_imsi_nochan_cgi() ); execute( TC_paging_imsi_nochan_lac_ci() ); execute( TC_paging_imsi_nochan_ci() ); execute( TC_paging_imsi_nochan_lai() ); execute( TC_paging_imsi_nochan_lac() ); execute( TC_paging_imsi_nochan_all() ); execute( TC_paging_imsi_nochan_plmn_lac_rnc() ); execute( TC_paging_imsi_nochan_rnc() ); execute( TC_paging_imsi_nochan_lac_rnc() ); execute( TC_paging_imsi_nochan_lacs() ); execute( TC_paging_imsi_nochan_lacs_empty() ); execute( TC_paging_imsi_nochan_cgi_unknown_cid() ); execute( TC_paging_imsi_a_reset() ); execute( TC_paging_imsi_load() ); execute( TC_paging_counter() ); execute( TC_paging_resp_unsol() ); execute( TC_rsl_drop_counter() ); execute( TC_rsl_unknown_unit_id() ); execute( TC_oml_unknown_unit_id() ); execute( TC_classmark() ); execute( TC_common_id() ); execute( TC_unsol_ass_fail() ); execute( TC_unsol_ass_compl() ); execute( TC_unsol_ho_fail() ); execute( TC_err_82_short_msg() ); execute( TC_err_84_unknown_msg() ); execute( TC_ho_int() ); execute( TC_ho_out_of_this_bsc() ); execute( TC_ho_out_fail_no_msc_response() ); execute( TC_ho_out_fail_rr_ho_failure() ); execute( TC_ho_out_fail_no_result_after_ho_cmd() ); execute( TC_ho_into_this_bsc() ); if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { execute( TC_ho_into_this_bsc_tla_v6() ); } execute( TC_ho_in_fail_msc_clears() ); execute( TC_ho_in_fail_msc_clears_after_ho_detect() ); execute( TC_ho_in_fail_no_detect() ); execute( TC_ho_in_fail_no_detect2() ); execute( TC_ho_neighbor_config_1() ); execute( TC_ho_neighbor_config_2() ); execute( TC_ho_neighbor_config_3() ); execute( TC_ho_neighbor_config_4() ); execute( TC_ho_neighbor_config_5() ); execute( TC_ho_neighbor_config_6() ); execute( TC_ho_neighbor_config_7() ); execute( TC_bssap_rlsd_does_not_cause_bssmap_reset() ); execute( TC_bssmap_clear_does_not_cause_bssmap_reset() ); execute( TC_ms_rel_ind_does_not_cause_bssmap_reset() ); execute( TC_dyn_pdch_ipa_act_deact() ); execute( TC_dyn_pdch_ipa_act_nack() ); execute( TC_dyn_pdch_osmo_act_deact() ); execute( TC_dyn_pdch_osmo_act_nack() ); execute( TC_chopped_ipa_ping() ); execute( TC_chopped_ipa_payload() ); /* Power control related */ execute( TC_assignment_verify_ms_power_params_ie() ); /* MSC pooling */ /* FIXME: in SCCPlite, indicating how many MSCs should be connected does currently not work. Since * RESET->RESET-ACK is unconditionally negotiated for all configured MSCs, they always all appear as connected * to osmo-bsc. The MSC pooling tests however require disconnecting selected MSCs, and hence don't work out as * intended on SCCPlite. So for now, run these only for SCCP/M3UA. */ if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) { execute( TC_mscpool_L3Compl_on_1_msc() ); execute( TC_mscpool_L3Complete_by_imsi_round_robin() ); execute( TC_mscpool_LU_by_tmsi_null_nri_0_round_robin() ); execute( TC_mscpool_LU_by_tmsi_null_nri_1_round_robin() ); execute( TC_mscpool_L3Complete_by_tmsi_unassigned_nri_round_robin() ); execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin() ); execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_1() ); execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_2() ); execute( TC_mscpool_LU_by_tmsi_from_other_PLMN() ); execute( TC_mscpool_paging_and_response_imsi() ); execute( TC_mscpool_paging_and_response_tmsi() ); execute( TC_mscpool_no_allow_attach_round_robin() ); execute( TC_mscpool_no_allow_attach_valid_nri() ); } /* at bottom as they might crash OsmoBSC before OS#3182 is fixed */ execute( TC_early_conn_fail() ); execute( TC_late_conn_fail() ); /* Emergency call handling (deny / allow) */ execute( TC_assignment_emerg_setup_allow() ); execute( TC_assignment_emerg_setup_deny_msc() ); execute( TC_assignment_emerg_setup_deny_bts() ); execute( TC_emerg_premption() ); /* Frequency hopping parameters handling */ execute( TC_fh_params_chan_activ() ); execute( TC_fh_params_imm_ass() ); execute( TC_fh_params_assignment_cmd() ); execute( TC_fh_params_handover_cmd() ); execute( TC_fh_params_si4_cbch() ); if (mp_enable_lcs_tests) { execute( TC_lcs_loc_req_for_active_ms() ); execute( TC_lcs_loc_req_for_active_ms_ta_req() ); execute( TC_lcs_loc_req_for_idle_ms() ); execute( TC_lcs_loc_req_no_subscriber() ); execute( TC_lcs_loc_req_for_active_ms_le_timeout() ); execute( TC_lcs_loc_req_for_active_ms_le_timeout2() ); execute( TC_lcs_loc_req_for_idle_ms_no_pag_resp() ); execute( TC_cm_service_during_lcs_loc_req() ); execute( TC_ho_during_lcs_loc_req() ); } } }