You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
osmo-ttcn3-hacks/bsc/BSC_Tests.ttcn

12691 lines
438 KiB

module BSC_Tests {
/* Integration Tests for OsmoBSC
* (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
* 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.
*/
friend module BSC_Tests_VAMOS;
friend module BSC_Tests_CBSP;
friend module BSC_Tests_LCLS;
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_TRX := 4;
const integer NUM_BTS := 3;
const integer NUM_BTS_CFG := 4; /* we have 4 BTS in the osmo-bsc.cfg (for inter-BSC HO tests) but use only 3 */
const integer NUM_TRX_CFG := 1; /* we support up to 4 TRX per BTS, but have only 1 TRX per BTS in osmo-bsc.cfg */
const integer NUM_MSC := 3;
const integer NUM_MGW := 2;
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;
friend type record BtsTrxIdx {
uint8_t bts,
uint8_t trx
}
private type record BtsParams {
integer trx_num,
integer tsc
}
/* Default Training Sequence Code expected for bts[i]:
* BTS 0 has BSIC 10 (and no explicit timeslot training_sequence_code config), so expecting TSC = (BSIC & 7) = 2.
* BTS 1 has BSIC 11, TSC = (BSIC & 7) = 3.
* BTS 2 has BSIC 12, TSC = (BSIC & 7) = 4.
* BTS 2 has BSIC 12, TSC = (BSIC & 7) = 4.
*/
private const BtsParams c_BtsParams[NUM_BTS_CFG] := {
/* BTS0 */ { trx_num := 1, tsc := 2 },
/* BTS1 */ { trx_num := 1, tsc := 3 },
/* BTS2 */ { trx_num := 4, tsc := 4 },
/* BTS3 */ { trx_num := 1, tsc := 4 }
}
private const RSL_IE_Body c_mr_conf_5_90 :=
valueof(RSL_IE_Body:{multirate_cfg := ts_RSL_MultirateCfg(true, 0, '00000100'B /* 5,90k */)});
/* 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 }
};
/* List of global mscpool counters, not related to a specific 'msc' entity. */
const CounterNameVals counternames_bsc_mscpool := {
{ "mscpool:subscr:no_msc", 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 }
};
const CounterNameVals counternames_bts_handover := {
{ "incoming_intra_bsc_ho:attempted", 0 },
{ "incoming_intra_bsc_ho:completed", 0 },
{ "incoming_intra_bsc_ho:stopped", 0 },
{ "incoming_intra_bsc_ho:no_channel", 0 },
{ "incoming_intra_bsc_ho:timeout", 0 },
{ "incoming_intra_bsc_ho:failed", 0 },
{ "incoming_intra_bsc_ho: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,
SystemInformationType13 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 := {
rest_octets := {
presence := '1'B,
bcch_change_mark := ?,
si_change_field := '0000'B,
presence2 := '0'B,
si13_change_mark := omit,
gprs_ma := omit,
zero := '0'B, /* PBCCH not present in cell */
rac := 0,
spgc_ccch_sup := '0'B,
priority_access_thr := '110'B,
network_control_order := '00'B,
gprs_cell_opts := {
nmo := '01'B,
t3168 := '011'B,
t3192 := '010'B,
drx_timer_max := '011'B,
access_burst_type := '0'B,
control_ack_type := '1'B,
bs_cv_max := 15,
pan_presence := '1'B,
pan_dec := 1,
pan_inc := 1,
pan_max := '111'B,
ext_info_presence := ?,
ext_info_length := *,
ext_info := *
},
gprs_pwr_ctrl_params := {
alpha := 0,
t_avg_w := '10000'B,
t_avg_t := '10000'B,
pc_meas_chan := '0'B,
n_avg_i := '1000'B
}
}
},
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 := {
pch_nch_info := ?,
vbs_vgcs_options := ?,
dtm_support := '0'B,
rac := omit,
max_lapdm := omit,
band_ind := '0'B /* C0 ARFCN indicates 1800 band */
}
}
};
/* 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.si13;
}
} 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);
}
}
friend function gen_l3_valid_payload(hexstring imsi := ''H) return octetstring {
var octetstring l3_payload;
if (lengthof(imsi) == 0) {
imsi := f_rnd_imsi('00101'H);
}
l3_payload := enc_PDU_ML3_MS_NW(valueof(ts_LU_REQ(LU_Type_IMSI_Attach, ts_MI_LV(ts_MI_IMSI(imsi)))));
return l3_payload;
}
type component test_CT extends CTRL_Adapter_CT {
/* Array of per-BTS/TRX state */
var BTS_State bts[NUM_BTS][NUM_TRX];
/* RSL common Channel Port (for RSL_Emulation) */
port RSL_CCHAN_PT RSL_CCHAN[NUM_BTS];
/* array of per-BTS/TRX RSL test ports */
port IPA_RSL_PT IPA_RSL[NUM_BTS][NUM_TRX];
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;
/* Configure/manage IPA_Emulation per-BTS/TRX port: */
port IPA_CFG_PT IPA_CFG_PORT[NUM_BTS][NUM_TRX];
var MGCP_Emulation_CT vc_MGCP[NUM_MGW];
var integer g_nr_mgw; /* number of vc_MGCP to initialize */
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;
var boolean g_handler_mode := false;
/* Osmux is enabled through VTY */
var boolean g_osmux_enabled_cn := false;
var boolean g_osmux_enabled_bts := 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 := {};
}
type record of charstring phys_chan_configs;
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
}
};
/* Must match per BTS config in osmo-bsc.cfg */
phys_chan_configs phys_chan_config := {
"CCCH+SDCCH4+CBCH",
"TCH/F",
"TCH/F",
"TCH/F",
"TCH/F",
"TCH/H",
"PDCH",
"PDCH"
};
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;
/* Value set in osmo-bsc.cfg "ms max power" */
uint8_t mp_exp_ms_power_level := 7;
/* Whether to check for memory leaks */
boolean mp_verify_talloc_count := true;
}
friend 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.expect_tsc := c_BtsParams[0].tsc;
pars.imsi := f_rnd_imsi('00101'H);
pars.imei := f_rnd_imei('00101'H);
log(testcasename(), ": using IMSI ", pars.imsi);
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_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);
}
function f_ctrs_bsc_and_bts_init(integer bts_count := NUM_BTS, CounterNameVals counternames := counternames_bsc_bts_handover) runs on test_CT {
f_ctrs_bts_init(bts_count, counternames);
f_ctrs_bsc_init(counternames);
}
private function f_ctrs_bsc_and_bts_handover_init(integer bts_count := NUM_BTS) runs on test_CT {
var CounterNameVals bts_names := counternames_bsc_bts_handover & counternames_bts_handover;
f_ctrs_bts_init(bts_count, bts_names);
f_ctrs_bsc_init(counternames_bsc_bts_handover);
}
private function f_ctrs_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);
}
private function f_ctrs_bsc_and_bts_add(integer bts_nr, charstring countername, integer val := 1) runs on test_CT {
f_ctrs_bts_add(bts_nr, countername, val);
f_ctrs_bsc_add(countername, val);
}
function f_ctrs_bts_verify() runs on test_CT {
f_counter_name_vals_expect_n(IPA_CTRL, "bts", g_ctr_bts);
}
/* 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_ctrs_bts_verify();
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();
}
friend function f_shutdown_helper(boolean ho := false) runs on test_CT {
/* Run the subscr and conn leak test only when the VTY is initialized */
if (BSCVTY.checkstate("Mapped") and mp_verify_talloc_count) {
f_verify_talloc_count(BSCVTY, {"struct bsc_subscr", "struct gsm_subscriber_connection"});
}
/* Reset handover related configuration */
if (ho) {
f_bts_0_cfg(BSCVTY,
{"no neighbors",
"neighbor-list mode manual-si5",
"neighbor-list add arfcn 100",
"neighbor-list add arfcn 200",
"si5 neighbor-list add arfcn 10",
"si5 neighbor-list add arfcn 20",
"handover algorithm 1"});
}
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_cn)));
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_cn))) {
log("BSSMAP: Received RESET-ACK in response to RESET, we're ready to go!");
}
[] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(g_osmux_enabled_cn))) -> value ud_ind {
log("BSSMAP: 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_cn)));
reset_received := true;
repeat;
}
[] BSSAP.receive { repeat; }
[] T.timeout {
log("BSSMAP: 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 idx BTS/TRX index values
* \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,
BtsTrxIdx idx := {0, 0}, boolean handler_mode := false)
runs on test_CT {
timer T := 10.0;
clnt.id := "IPA-BTS" & int2str(idx.bts) & "-TRX" & int2str(idx.trx) & "-RSL";
clnt.vc_IPA := IPA_Emulation_CT.create(clnt.id & "-IPA") alive;
clnt.ccm_pars := c_IPA_default_ccm_pars;
clnt.ccm_pars.name := "Osmocom TTCN-3 BTS Simulator";
clnt.ccm_pars.unit_id := int2str(1234 + idx.bts) & "/0/" & int2str(idx.trx);
if (handler_mode) {
clnt.vc_RSL := RSL_Emulation_CT.create(clnt.id & "-RSL") alive;
connect(clnt.vc_RSL:CCHAN_PT, self:RSL_CCHAN[idx.bts]);
}
map(clnt.vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
connect(clnt.vc_IPA:CFG_PORT, self:IPA_CFG_PORT[idx.bts][idx.trx]);
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[idx.bts][idx.trx]);
}
var integer local_port := 10000 + idx.bts * 1000 + idx.trx;
clnt.vc_IPA.start(IPA_Emulation.main_client(bsc_host, bsc_port, "", local_port, 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[idx.bts][idx.trx].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) {
T.stop;
}
[] IPA_RSL[idx.bts][idx.trx].receive(ASP_IPA_Event:?) { repeat }
[] IPA_RSL[idx.bts][idx.trx].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, BtsTrxIdx idx := {0, 0}) runs on test_CT {
var IPL4asp_Types.Result res := {
errorCode := omit,
connId := omit,
os_error_code := omit,
os_error_text := omit
};
if (not isbound(clnt) or not isbound(clnt.vc_IPA)) {
return;
}
/* Alive components don't finish sockets (TCP FIN) when they are
* stopped. Hence, we need to manually call close() on them to make sure
* the IUT knows about it. */
f_ipa_cfg_disconnect(IPA_CFG_PORT[idx.bts][idx.trx], res);
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_cn))) -> value ud_ind {
BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress,
ts_BSSMAP_ResetAck(g_osmux_enabled_cn)));
repeat;
}
}
altstep no_bssmap_reset() runs on test_CT {
[] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset(g_osmux_enabled_cn))) {
setverdict(fail, "unexpected BSSMAP Reset");
mtc.stop;
}
}
function f_init_mgcp(integer mgw_nr, charstring id) runs on test_CT {
id := id & "-MGCP-" & int2str(mgw_nr);
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 + mgw_nr,
/* Enable it for SCCPlite, since we have 2 MGCP sockets towards MGW (UDP one +
the one with MGCP over IPA forwarded from MSC one) */
multi_conn_mode := (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_SCCPlite_SERVER)
};
vc_MGCP[mgw_nr] := MGCP_Emulation_CT.create(id) alive;
vc_MGCP[mgw_nr].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_cn(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");
}
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)});
}
friend function f_logp(TELNETasp_PT pt, charstring log_msg)
{
// log on TTCN3 log output
log(log_msg);
// log in stderr log
if (pt.checkstate("Mapped")) {
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][0].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][0].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][0].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][0].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");
}
}
private function f_bssap_idx_init(integer bssap_idx) runs on test_CT {
/* Call a function of our 'parent component' RAN_Adapter_CT to start the
* MSC-side BSSAP emulation */
if (g_handler_mode) {
var RanOps ranops := MSC_RanOps;
ranops.use_osmux := g_osmux_enabled_cn;
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();
}
}
private function f_bssap_idx_disconnect(integer bssap_idx) runs on test_CT {
f_ran_adapter_cleanup(g_bssap[bssap_idx]);
}
/* 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,
integer nr_msc := 1, integer nr_mgw := 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");
f_vty_allow_osmux_cn(g_osmux_enabled_cn);
var my_BooleanList allow_attach := { false, false, false };
f_init_statsd("VirtMSC", vc_STATSD, mp_test_ip, mp_bsc_statsd_port);
/* Make sure each MSC's internal state is "DISCONNECTED" at first */
for (bssap_idx := 0; bssap_idx < NUM_MSC; bssap_idx := bssap_idx+1) {
f_vty_transceive(BSCVTY, "msc " & int2str(bssap_idx) & " bssmap reset", strict := false);
}
g_handler_mode := handler_mode;
for (bssap_idx := 0; bssap_idx < nr_msc; bssap_idx := bssap_idx+1) {
allow_attach[bssap_idx] := true;
f_bssap_idx_init(bssap_idx);
}
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_client(mp_bsc_ip, mp_bsc_ctrl_port);
g_nr_mgw := nr_mgw;
for (var integer i := 0; i < g_nr_mgw; i := i+1) {
f_init_mgcp(i, "VirtMGW");
}
for (var integer i := 0; i < nr_bts; i := i+1) {
f_init_bts(i, c_BtsParams[i].trx_num, handler_mode);
}
/* Emit a marker to appear in the SUT's own logging output */
f_logp(BSCVTY, testcasename() & "() start");
}
function f_init_bts(integer bts_idx := 0,
integer trx_num := NUM_TRX_CFG,
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(s) */
for (var integer trx_idx := 0; trx_idx < trx_num; trx_idx := trx_idx + 1) {
f_ipa_rsl_start(bts[bts_idx][trx_idx].rsl,
mp_bsc_ip, mp_bsc_rsl_port,
{bts_idx, trx_idx}, handler_mode);
}
/* wait until BSC tells us "connected" */
f_wait_oml(bts_idx, "connected", 5.0);
/* Set up BTS with VTY commands: */
f_vty_enter_cfg_bts(BSCVTY, bts_idx);
if (g_osmux_enabled_bts) {
f_vty_transceive(BSCVTY, "osmux on");
} else {
f_vty_transceive(BSCVTY, "osmux off");
}
f_vty_transceive(BSCVTY, "end");
}
function f_init_bts_and_check_sysinfo(integer bts_idx := 0,
integer trx_num := NUM_TRX_CFG,
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, trx_num, 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 / TRX */
function f_exp_ipa_rx(template (present) RSL_Message t_rx,
BtsTrxIdx idx := {0, 0},
float Tval := 2.0)
runs on test_CT return RSL_Message {
var ASP_RSL_Unitdata rx_rsl_ud;
timer T := Tval;
T.start;
alt {
[] IPA_RSL[idx.bts][idx.trx].receive(tr_ASP_RSL_UD(t_rx, ?)) -> value rx_rsl_ud {
T.stop;
}
[] IPA_RSL[idx.bts][idx.trx].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(template (value) RSL_Message t_tx,
BtsTrxIdx idx := {0, 0},
IpaStreamId sid := IPAC_PROTO_RSL_TRX0)
runs on test_CT {
IPA_RSL[idx.bts][idx.trx].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);
f_ipa_tx(ts_RSL_CHAN_RQD('23'O, 23));
rsl_unused := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV));
f_shutdown_helper();
}
const CounterNameVals counternames_bts_chreq := {
{ "chreq:total", 0 },
{ "chreq:attempted_emerg", 0 },
{ "chreq:attempted_call", 0 },
{ "chreq:attempted_location_upd", 0 },
{ "chreq:attempted_pag", 0 },
{ "chreq:attempted_pdch", 0 },
{ "chreq:attempted_other", 0 },
{ "chreq:attempted_unknown", 0 },
{ "chreq:successful", 0 },
{ "chreq:successful_emerg", 0 },
{ "chreq:successful_call", 0 },
{ "chreq:successful_location_upd", 0 },
{ "chreq:successful_pag", 0 },
{ "chreq:successful_pdch", 0 },
{ "chreq:successful_other", 0 },
{ "chreq:successful_unknown", 0 },
{ "chreq:no_channel", 0 },
{ "chreq:max_delay_exceeded", 0 }
};
/* verify the "chreq:*" counters */
private function f_chan_act_counter(OCT1 ra, charstring chreq_ctr_suffix) runs on test_CT
{
var GsmFrameNumber fn := 23;
f_logp(BSCVTY, "f_chan_act_counter(" & chreq_ctr_suffix & ")");
var RSL_Message rx_rsl;
f_ipa_tx(ts_RSL_CHAN_RQD(ra, fn));
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV));
var RslChannelNr chan_nr := rx_rsl.ies[0].body.chan_nr;
f_ctrs_bts_add(0, "chreq:total");
f_ctrs_bts_add(0, "chreq:attempted_" & chreq_ctr_suffix);
f_ctrs_bts_verify();
f_ipa_tx(ts_RSL_CHAN_ACT_ACK(chan_nr, fn+10));
rx_rsl := f_exp_ipa_rx(tr_RSL_IMM_ASSIGN(0));
f_ctrs_bts_add(0, "chreq:successful");
f_ctrs_bts_add(0, "chreq:successful_" & chreq_ctr_suffix);
f_ctrs_bts_verify();
/* test is done, release RSL Conn Fail Ind to clean up */
f_ipa_tx(ts_RSL_CONN_FAIL_IND(chan_nr, RSL_ERR_RADIO_LINK_FAIL));
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), Tval := 10.0);
f_ipa_tx(ts_RSL_RF_CHAN_REL_ACK(chan_nr));
f_sleep(1.0);
}
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);
f_vty_allow_emerg_bts(true, 0);
f_ctrs_bts_init(1, counternames_bts_chreq);
/* emergency call: RA & 0xe0 == 0xa0 --> CHREQ_T_EMERG_CALL */
f_chan_act_counter('a3'O, "emerg");
/* voice TCH/H: RA & 0xf0 == 0x40 --> CHREQ_T_VOICE_CALL_TCH_H */
f_chan_act_counter('43'O, "call");
/* LU: RA & 0xf0 == 0x00 --> CHREQ_T_LOCATION_UPD */
f_chan_act_counter('03'O, "location_upd");
/* Paging: RA & 0xf0 == 0x20 --> CHREQ_T_PAG_R_TCH_F */
f_chan_act_counter('23'O, "pag");
/* Paging: RA & 0xf0 == 0x30 --> CHREQ_T_PAG_R_TCH_FH */
f_chan_act_counter('33'O, "pag");
/* LU: RA & 0xfc == 0x78 --> CHREQ_T_PDCH_TWO_PHASE */
/* no PCU, so PDCH not allowed. Skip this test for now. */
/* f_chan_act_counter('7b'O, "pdch"); */
/* LU: RA & 0xf0 == 0x10 --> CHREQ_T_SDCCH */
f_chan_act_counter('13'O, "other");
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(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), Tval := 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][0].clear;
f_ipa_tx(ts_RSL_CHAN_RQD('A5'O, 23));
rx_rsl := f_exp_ipa_rx(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");
}
f_shutdown_helper();
}
/* 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;
var octetstring l3_payload := gen_l3_valid_payload();
f_init(1);
/* Send CHAN RQD and wait for allocation; acknowledge it */
var RslChannelNr chan_nr := f_chreq_act_ack();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3_payload));
BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload)));
/* 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(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;
var octetstring l3_payload := gen_l3_valid_payload();
f_init(1);
/* Send CHAN RQD and wait for allocation; acknowledge it */
var RslChannelNr chan_nr := f_chreq_act_ack();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3_payload));
BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload))) -> 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(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(ts_RSL_CHAN_RQD('33'O, 33));
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV));
var RslChannelNr chan_nr := rx_rsl.ies[0].body.chan_nr;
f_ipa_tx(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 44.018 Table 9.1.8.2:
* 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][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(ts_RSL_CHAN_RQD('42'O, 42));
alt {
[] IPA_RSL[0][0].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) {
setverdict(fail, "Received CHAN ACT ACK without resources?!?");
}
[] IPA_RSL[0][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][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][0].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL))) {
log("Received CHANnel RELease");
setverdict(pass);
}
[] IPA_RSL[0][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][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, gen_l3_valid_payload());
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 assignment 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_perform_clear_test_ct(dt);
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",
template (value) BSSMAP_IE_CellIdentifier cell_id_source := ts_CellID_LAC_CI(1, 1),
template (omit) BSSMAP_oldToNewBSSIEs oldToNewBSSIEs := omit,
template (omit) TestHdlrEncrParams enc := omit) return PDU_BSSAP {
var PDU_BSSAP ho_req;
var BSSMAP_IE_EncryptionInformation encryptionInformation :=
valueof(ts_BSSMAP_IE_EncrInfo('0000000000000000'O,'01'O));
var template (omit) BSSMAP_IE_ChosenEncryptionAlgorithm chosenEncryptionAlgorithm := omit;
var template (omit) BSSMAP_IE_KC128 kc128 := omit;
if (ispresent(enc)) {
var TestHdlrEncrParams v_enc := valueof(enc);
encryptionInformation := valueof(ts_BSSMAP_IE_EncrInfo(v_enc.enc_key, v_enc.enc_alg_permitted));
if (ispresent(v_enc.enc_alg_chosen)) {
chosenEncryptionAlgorithm := valueof(
ts_BSSMAP_IE_ChosenEncryptionAlgorithm(int2oct(enum2int(
f_cipher_mode_bssmap_to_rsl(v_enc.enc_alg_chosen)), 1)));
}
if (ispresent(v_enc.enc_kc128)) {
kc128 := ts_BSSMAP_IE_Kc128(v_enc.enc_kc128);
}
}
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,
cell_id_source := cell_id_source,
oldToNewBSSIEs := oldToNewBSSIEs,
encryptionInformation := encryptionInformation,
chosenEncryptionAlgorithm := chosenEncryptionAlgorithm,
kC128 := kc128,
/* on AoIP, allow "all" codecs (until we add more concise
* tests) */
codecList := ts_BSSMAP_IE_CodecList(
{ts_CodecAMR_F, ts_CodecAMR_H,
ts_CodecEFR, ts_CodecFR, ts_CodecHR})));
} else {
var BSSMAP_IE_CircuitIdentityCode cic := valueof(ts_BSSMAP_IE_CIC(0,1));
ho_req := valueof(ts_BSSMAP_HandoverRequest(cic, omit,
cell_id_source := cell_id_source,
oldToNewBSSIEs := oldToNewBSSIEs,
encryptionInformation := encryptionInformation,
chosenEncryptionAlgorithm := chosenEncryptionAlgorithm,
kC128 := kc128));
}
return ho_req;
}
/* generate an assignment complete template for either AoIP or SCCPlite */
function f_gen_exp_compl(integer bssap_idx := 0)
runs on MSC_ConnHdlr return template PDU_BSSAP {
var template PDU_BSSAP exp_compl;
if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) {
var template BSSMAP_IE_Osmo_OsmuxCID exp_osmux_cid := omit;
if (g_pars.use_osmux_cn) {
var template (present) INT1 exp_cid := ?;
if (isbound(g_media.mgcp_conn[0].local_osmux_cid) and isbound(g_media.mgcp_conn[1].local_osmux_cid)) {
exp_cid := (g_media.mgcp_conn[0].local_osmux_cid, g_media.mgcp_conn[1].local_osmux_cid);
} else if (isbound(g_media.mgcp_conn[0].local_osmux_cid)) {
exp_cid := g_media.mgcp_conn[0].local_osmux_cid;
} else if (isbound(g_media.mgcp_conn[1].local_osmux_cid)) {
exp_cid := g_media.mgcp_conn[1].local_osmux_cid;
}
exp_osmux_cid := tr_OsmuxCID(exp_cid);
}
exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, exp_osmux_cid);
} else {
/* CIC is optional "*" as the MSC allocated it */
exp_compl := tr_BSSMAP_AssignmentComplete(*, omit, 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 return DchanTuple {
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, gen_l3_valid_payload());
/* 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; }
}
return dt;
}
private function f_tc_assignment_csd(charstring data_rate_str, OCT1 data_rate, boolean transp := true) runs on MSC_ConnHdlr {
var template PDU_BSSAP exp_compl := f_gen_exp_compl();
var PDU_BSSAP ass_cmd := f_gen_ass_req();
var SDP_FIELD_PayloadType pt_csd := PT_CSD;
ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeCSD);
ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecCSData}));
/* Non-transparent service (3GPP TS 48.008 § 3.2.2.11, oct 5, bit 7) */
if (not transp) {
data_rate := data_rate or4b '40'O;
}
ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := data_rate;
log("-----------------------------------------------");
log("Assignment req with data rate: " & data_rate_str);
log("-----------------------------------------------");
f_establish_fully(ass_cmd, exp_compl);
if (g_media.bts.rtp_pt != enum2int(pt_csd)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("unexpected RTP payload type: ", g_media.bts.rtp_pt));
}
f_perform_clear();
f_create_mgcp_delete_ep(g_media.mgcp_ep);
}
private function f_tc_assignment_csd_all(charstring id) runs on MSC_ConnHdlr {
/* Data rates that require multi-slot HSCSD assignment are not tested
* on purpose (not supported): T_32k0, T_28k8, NT_43k5, NT_29k0 */
f_tc_assignment_csd("T_14k4", GSM0808_DATA_RATE_TRANSP_14k4);
f_tc_assignment_csd("T_9k6", GSM0808_DATA_RATE_TRANSP_9k6);
f_tc_assignment_csd("T_4k8", GSM0808_DATA_RATE_TRANSP_4k8);
f_tc_assignment_csd("T_2k4", GSM0808_DATA_RATE_TRANSP_2k4);
f_tc_assignment_csd("T_1k2", GSM0808_DATA_RATE_TRANSP_1k2);
f_tc_assignment_csd("T_600", GSM0808_DATA_RATE_TRANSP_600);
f_tc_assignment_csd("T_1200_75", GSM0808_DATA_RATE_TRANSP_1200_75);
f_tc_assignment_csd("NT_12000_6000", GSM0808_DATA_RATE_NON_TRANSP_12000_6000, false);
f_tc_assignment_csd("NT_14k5", GSM0808_DATA_RATE_NON_TRANSP_14k5, false);
f_tc_assignment_csd("NT_12k0", GSM0808_DATA_RATE_NON_TRANSP_12k0, false);
f_tc_assignment_csd("NT_6k0", GSM0808_DATA_RATE_NON_TRANSP_6k0, false);
}
testcase TC_assignment_csd() runs on test_CT {
if (Misc_Helpers.f_osmo_repo_is("nightly")) { /* osmo-bsc > 1.10.0 */
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, guard_timeout := 120.0);
f_sleep(1.0);
vc_conn := f_start_handler(refers(f_tc_assignment_csd_all), pars);
vc_conn.done;
} else {
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);
var DchanTuple dt := f_assignment_exp(ass_cmd, exp_fail, "BSC accepted Assignment for CSD");
f_perform_clear_test_ct(dt);
}
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);
var DchanTuple dt := f_assignment_exp(ass_cmd, exp_fail, "BSC accepted Assignment for Speech+CTM");
f_perform_clear_test_ct(dt);
f_shutdown_helper();
}
type record DchanTuple {
integer sccp_conn_id,
RslChannelNr rsl_chan_nr,
BtsTrxIdx idx
}
type record of DchanTuple DchanTuples;
/* Send CHAN RQD and wait for allocation; acknowledge it */
private function f_chreq_act_ack(OCT1 ra := '23'O,
GsmFrameNumber fn := 23,
BtsTrxIdx idx := {0, 0})
runs on test_CT return RslChannelNr {
var RSL_Message rx_rsl;
f_ipa_tx(ts_RSL_CHAN_RQD(ra, fn), {idx.bts, 0});
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV), idx);
var RslChannelNr chan_nr := rx_rsl.ies[0].body.chan_nr;
f_ipa_tx(ts_RSL_CHAN_ACT_ACK(chan_nr, fn+10), idx);
rx_rsl := f_exp_ipa_rx(tr_RSL_IMM_ASSIGN(0), {idx.bts, 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,
BtsTrxIdx idx := {0, 0})
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, idx);
f_ipa_tx(ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3), idx);
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));
dt.idx := idx;
return dt;
}
/* Like f_est_dchan(), but for the first lchan of a dynamic timeslot: first ACK the deactivation of PDCH. */
function f_est_dchan_dyn(OCT1 ra, GsmFrameNumber fn, octetstring l3,
BtsTrxIdx idx := {0, 0})
runs on test_CT return DchanTuple {
var BSSAP_N_CONNECT_ind rx_c_ind;
var DchanTuple dt;
/* Send CHAN RQD */
var RSL_Message rx_rsl;
f_ipa_tx(ts_RSL_CHAN_RQD(ra, fn), {idx.bts, 0});
/* The dyn TS first deactivates PDCH */
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), idx, Tval := T3101_MAX);
dt.rsl_chan_nr := rx_rsl.ies[0].body.chan_nr;
f_ipa_tx(ts_RSL_RF_CHAN_REL_ACK(dt.rsl_chan_nr), idx);
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV), idx);
dt.rsl_chan_nr := rx_rsl.ies[0].body.chan_nr;
/* Now activates the signalling channel */
f_ipa_tx(ts_RSL_CHAN_ACT_ACK(dt.rsl_chan_nr, fn+10), idx);
rx_rsl := f_exp_ipa_rx(tr_RSL_IMM_ASSIGN(0), {idx.bts, 0});
f_ipa_tx(ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3), idx);
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));
dt.idx := idx;
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)
runs on test_CT {
var RSL_Message rx_rsl;
/* expect BSC to disable the channel */
rx_rsl := f_exp_ipa_rx(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), dt.idx, Tval := T3101_MAX);
/* respond with CHAN REL ACK */
f_ipa_tx(ts_RSL_RF_CHAN_REL_ACK(dt.rsl_chan_nr), dt.idx);
/* 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, gen_l3_valid_payload());
/* simulate RLL REL IND */
f_ipa_tx(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);
/* 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, gen_l3_valid_payload());
/* Sending CONN FAIL IND immediately may trigger a race condition.
* Give the BSC some time to process a new SCCP connection (OS#5823). */
f_sleep(0.2);
/* simulate CONN FAIL IND */
f_ipa_tx(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);
/* 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(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(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), Tval := 10.0);
/* BTS<-BSC: respond with CHAN REL ACK */
f_ipa_tx(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, gen_l3_valid_payload());
/* 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(tr_RSL_DEACT_SACCH(dt.rsl_chan_nr));
/* BTS->BSC: simulate a late CONN FAIL IND from BTS */
f_ipa_tx(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(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL), Tval := 10.0);
/* BTS->BSC: respond with CHAN REL ACK */
f_ipa_tx(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();
}
private function f_TC_stats_conn_fail(charstring id) runs on MSC_ConnHdlr {
var template PDU_BSSAP exp_fail := tr_BSSMAP_AssignmentFail;
var PDU_BSSAP ass_cmd := f_gen_ass_req();
f_statsd_reset();
/* Establish SDCCH (invalid DataIndicator for exp_fail) */
ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelTypeCSD);
ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechId_DataIndicator := 'ff'O;
f_establish_fully(ass_cmd, exp_fail);
/* Expect stats to be 0 */
var StatsDExpects expect := {
{name := "TTCN3.bts.0.chan.rf_fail", mtype := "c", min := 0, max := 0},
{name := "TTCN3.bts.0.chan.rf_fail_sdcch", mtype := "c", min := 0, max := 0}
};
f_statsd_expect(expect);
/* Simulate CONN FAIL IND on SDCCH */
RSL.send(ts_ASP_RSL_UD(
ts_RSL_CONN_FAIL_IND(g_chan_nr, RSL_ERR_RADIO_LINK_FAIL),
IPAC_PROTO_RSL_TRX0));
f_sleep(1.0);
/* Expect stats to be 1 */
expect := {
{name := "TTCN3.bts.0.chan.rf_fail", mtype := "c", min := 1, max := 1},
{name := "TTCN3.bts.0.chan.rf_fail_sdcch", mtype := "c", min := 1, max := 1}
};
f_statsd_expect(expect);
BSSAP.receive(tr_BSSMAP_ClearRequest);
f_perform_clear();
}
testcase TC_stats_conn_fail() 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_stats_conn_fail), pars);
vc_conn.done;
f_shutdown_helper();
}
function f_expect_chan_rel(RslChannelNr rsl_chan_nr,
BtsTrxIdx idx := {0, 0},
boolean expect_deact_sacch := true,
boolean expect_rr_chan_rel := true,
boolean expect_rll_rel_req := true,
boolean handle_rll_rel := true,
template CellSelIndValue expect_cells := omit,
template (present) 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[idx.bts][idx.trx].receive(tr_ASP_RSL_UD(tr_RSL_DEACT_SACCH(rsl_chan_nr))) {
got_deact_sacch := true;
repeat;
}
[] IPA_RSL[idx.bts][idx.trx].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 (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(expect_cells, "omit")) {
var CellSelIndValue cells := dec_CellSelIndValue(
l3.msgs.rrm.channelRelease.cellSelectionIndicator.cellSelectionIndicatorValue);
log("GOT RR CHANNEL RELEASE WITH CELLS: ", cells);
if (match(cells, expect_cells)) {
setverdict(pass);
} else {
log("EXPECTED CELLS: ", expect_cells);
setverdict(fail, "Received cells list on RR Channel Release does not match expectations");
}
}
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[idx.bts][idx.trx].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(ts_RSL_REL_CONF(rsl_chan_nr, main_dcch));
}
repeat;
}
[] IPA_RSL[idx.bts][idx.trx].receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL))) {
/* respond with CHAN REL ACK */
f_ipa_tx(ts_RSL_RF_CHAN_REL_ACK(rsl_chan_nr));
}
/* ignore any user data */
[] IPA_RSL[idx.bts][idx.trx].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, gen_l3_valid_payload());
/* 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(dt.rsl_chan_nr, expect_rll_rel_req := false);
f_shutdown_helper();
}
function f_TC_chan_rel_last_eutran_plmn_hard_clear(boolean tx_csfb_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, gen_l3_valid_payload());
/* Send CommonID with some random PLMN (BSC doesn't take it into account
/* yet when generating the EUTRAN neigh list in RR CHannel Release) */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_CommonId('001019876543210'H, '323454'O)));
/* Instruct BSC to clear channel */
var BssmapCause cause := 0;
if (tx_csfb_ind) {
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_ClearCommandCSFB(cause)));
} else {
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));
}
/* 1 neighbor is added by default in osmo-bts.cfg and
SystemInformationConfig_default, use that: */
var template CellSelIndValue exp_cells := f_tr_rr_chan_rel_earfcns(1);
f_expect_chan_rel(dt.rsl_chan_nr, expect_rll_rel_req := false, expect_cells := exp_cells);
f_shutdown_helper();
}
/* Test behavior of RR Channel rRelease after Clear Command without CSFB indicator
from MSC, previously receiving any CommonID containing the "Last Used E-UTRAN
PLMN Id". According to spec (3GPP TS 48.008 sec 3.1.30) that's the bit requesting
EUTRAN neighbor list sent later on by BSC in RR Channel, so receiving CSFB
Indicator or not shouldn't matter at all. */
testcase TC_chan_rel_last_eutran_plmn_hard_clear_no_csfb() runs on test_CT {
f_TC_chan_rel_last_eutran_plmn_hard_clear(false);
}
/* Test behavior of RR Channel rRelease after Clear Command with CSFB indicator from
MSC, previously receiving any CommonID containing the "Last Used E-UTRAN PLMN
Id". According to spec (3GPP TS 48.008 sec 3.1.30) that's the bit requesting
EUTRAN neighbor list sent later on by BSC in RR Channel. */
testcase TC_chan_rel_last_eutran_plmn_hard_clear_csfb() runs on test_CT {
f_TC_chan_rel_last_eutran_plmn_hard_clear(true);
}
/* Test behavior of RR Channel Release after Clear Command with CSFB indicator from
MSC, without receiving any CommonID containing the "Last Used E-UTRAN PLMN
Id". According to spec (TS 48.008 version 16.0.0 Release 16 "3.2.1.21") the
CSFB Indicator should not be used anymore, and hence, there should be no
EUTRAN neighbor list sent by BSC in RR Channel release since no CommonId with
Last Used E-UTRAN PLMN Id" IE was sent for this conn. */
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, gen_l3_valid_payload());
/* 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(dt.rsl_chan_nr, expect_rll_rel_req := false);
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, gen_l3_valid_payload());
/* release the SCCP connection */
BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0));
f_expect_chan_rel(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, gen_l3_valid_payload());
/* release the SCCP connection */
BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0));
f_expect_chan_rel(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, gen_l3_valid_payload());
/* Clear the queue, it might still contain stuff like IMMEDIATE ASSIGN */
IPA_RSL[0][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_cn)));
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_cn))) { }
[] BSSAP.receive(tr_BSSAP_DISC_ind(dt.sccp_conn_id, ?, ?)) { }
}
f_expect_chan_rel(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, gen_l3_valid_payload());
f_expect_chan_rel(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 (present) RR_Cause expect_rr_cause)
runs on test_CT
{
var DchanTuple dt;
dt := f_est_dchan('23'O, 23, gen_l3_valid_payload());
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(dt.rsl_chan_nr, expect_rll_rel_req := false, expect_rr_cause := expect_rr_cause);
}
/* 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);
f_shutdown_helper();
}
/* 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 RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(6));
var octetstring l3_payload := gen_l3_valid_payload();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(0)), l3_payload));
T.start;
alt {
[] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload))) {
setverdict(fail, "MSC received COMPL L3 for non-active lchan");
}
[] BSSAP.receive {}
[] IPA_RSL[0][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;
var octetstring l3_payload;
f_init(1);
chan_nr := f_chreq_act_ack();
l3_payload := gen_l3_valid_payload();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(1)), l3_payload));
timer T := 2.0;
T.start;
alt {
[] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload))) {
setverdict(fail, "MSC received COMPL L3 for invalid SAPI 1");
}
[] BSSAP.receive { repeat; }
[] IPA_RSL[0][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_payload := gen_l3_valid_payload();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_DCCH(3)), l3_payload));
T.start;
alt {
[] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload))) {
setverdict(fail, "MSC received COMPL L3 for invalid SAPI 3");
}
[] BSSAP.receive { repeat; }
[] IPA_RSL[0][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_payload := gen_l3_valid_payload();
f_ipa_tx(ts_RSL_EST_IND(chan_nr, valueof(ts_RslLinkID_SACCH(0)), l3_payload));
T.start;
alt {
[] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(l3_payload))) {
setverdict(fail, "MSC received COMPL L3 for invalid Link SACCH");
}
[] BSSAP.receive { repeat; }
[] IPA_RSL[0][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);
}
f_perform_clear();
}
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 (present) myBSSMAP_Cause cause := ?,
template (present) BIT2 cc := ?,
float T_val := 2.0)
runs on test_CT {
var BSSAP_N_DATA_ind rx_di;
timer T;
var template (present) BSSMAP_IE_Cause tr_cause := tr_BSSMAP_IE_Cause(cause);
var template (present) 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);
}
/* Who ever on the earth decided to define this field as two separate bits?!? */
var BIT2 rx_cc := rx_di.userData.pdu.bssmap.sAPInReject.dLCI.c2
& rx_di.userData.pdu.bssmap.sAPInReject.dLCI.c1;
if (not match(rx_cc, cc)) {
setverdict(fail, "Rx unexpected Control Channel type: ",
rx_cc, " vs expected ", cc);
}
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 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, gen_l3_valid_payload());
/* MSC sends some data on (not yet established) SAPI=3 link */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(gen_l3_valid_payload(), '03'O)));
/* BSC attempts to establish a SAPI=3 link on DCCH */
rx_rsl := f_exp_ipa_rx(tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3)));
/* MS sends unexpected RELease INDication on SAPI=3 */
f_ipa_tx(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(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 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, gen_l3_valid_payload());
/* MSC sends some data on (not yet established) SAPI=3 link */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(gen_l3_valid_payload(), '03'O)));
/* BSC attempts to establish a SAPI=3 link on DCCH */
rx_rsl := f_exp_ipa_rx(tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3)));
/* BTS sends unexpected ERROR INDication on SAPI=3 */
f_ipa_tx(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(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 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, gen_l3_valid_payload());
/* MSC sends some data on (not yet established) SAPI=3 link */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(gen_l3_valid_payload(), '03'O)));
/* BSC attempts to establish a SAPI=3 link on DCCH */
rx_rsl := f_exp_ipa_rx(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(dt.rsl_chan_nr, expect_rll_rel_req := false);
f_shutdown_helper();
}
/* Check DLCI CC (Control Channel type) bits in SAPI N Reject */
testcase TC_rll_sapi_n_reject_dlci_cc() runs on test_CT {
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, gen_l3_valid_payload());
/* MSC sends some data on (not yet established) SAPI=3 link */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSAP_DTAP(gen_l3_valid_payload(), '03'O)));
/* BSC attempts to establish a SAPI=3 link on DCCH */
rx_rsl := f_exp_ipa_rx(tr_RSL_EST_REQ(dt.rsl_chan_nr, tr_RslLinkID_DCCH(3)));
/* MS sends unexpected ERROR INDication on DCCH/ACCH SAPI=3 */
f_ipa_tx(ts_RSL_ERROR_IND(dt.rsl_chan_nr, ts_RslLinkID_DCCH(3), ''O));
f_exp_sapi_n_reject(3, GSM0808_CAUSE_BSS_NOT_EQUIPPED, '10'B);
/* Clean up the connection */
BSSAP.send(ts_BSSAP_DISC_req(dt.sccp_conn_id, 0));
f_expect_chan_rel(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);
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, gen_l3_valid_payload());
/* Send CommonID with some random PLMN (BSC doesn't take it into account
* yet when generating the EUTRAN neigh list in RR CHannel Release) */
BSSAP.send(ts_BSSAP_DATA_req(dt.sccp_conn_id, ts_BSSMAP_CommonId('001019876543210'H, '323454'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(dt.rsl_chan_nr, expect_rll_rel_req := false, 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][0].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;
}
[] IPA_RSL[rsl_idx][0].receive { repeat; }
[] 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();
}
/* Verify correct stats on the number of configured and connected MSCs */
private function f_tc_stat_num_msc_connected_msc_connhdlr(integer expect_num_msc_connected) runs on MSC_ConnHdlr {
g_pars := f_gen_test_hdlr_pars();
var StatsDExpects expect := {
{ name := "TTCN3.bsc.0.num_msc.connected", mtype := "g", min := expect_num_msc_connected, max := expect_num_msc_connected },
{ name := "TTCN3.bsc.0.num_msc.total", mtype := "g", min := NUM_MSC, max := NUM_MSC }
};
f_statsd_expect(expect);
}
private function f_tc_stat_num_msc_connected_test_ct(void_fn tc_fn, integer nr_msc) runs on test_CT
{
var MSC_ConnHdlr vc_conn;
f_init(nr_bts := 1, handler_mode := true, nr_msc := nr_msc);
f_sleep(1.0);
vc_conn := f_start_handler(tc_fn);
vc_conn.done;
/* Also verify stat exposed on CTRL interface */
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_msc:connected", int2str(nr_msc));
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_msc:total", int2str(NUM_MSC));
f_shutdown_helper();
}
/* Verify that when 1 MSC is active, that num_msc:connected reports 1. */
private function f_tc_stat_num_msc_connected_1(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_msc_connected_msc_connhdlr(1);
}
testcase TC_stat_num_msc_connected_1() runs on test_CT {
f_tc_stat_num_msc_connected_test_ct(refers(f_tc_stat_num_msc_connected_1), 1);
}
/* Verify that when 2 MSCs are active, that num_msc:connected reports 2. */
private function f_tc_stat_num_msc_connected_2(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_msc_connected_msc_connhdlr(2);
}
testcase TC_stat_num_msc_connected_2() runs on test_CT {
f_tc_stat_num_msc_connected_test_ct(refers(f_tc_stat_num_msc_connected_2), 2);
}
/* Verify that when 3 MSCs are active, that num_msc:connected reports 3. */
private function f_tc_stat_num_msc_connected_3(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_msc_connected_msc_connhdlr(3);
}
testcase TC_stat_num_msc_connected_3() runs on test_CT {
f_tc_stat_num_msc_connected_test_ct(refers(f_tc_stat_num_msc_connected_3), 3);
}
/* Verify correct stats on the number of configured and connected MSCs */
private function f_tc_stat_num_bts_connected_msc_connhdlr(integer expect_num_bts_connected) runs on MSC_ConnHdlr {
var integer num_trx_connected := 0;
var integer num_trx_total := 0;
for (var integer i := 0; i < lengthof(c_BtsParams); i := i + 1) {
var integer trx_num := c_BtsParams[i].trx_num;
num_trx_total := num_trx_total + trx_num;
if (i < expect_num_bts_connected) {
num_trx_connected := num_trx_connected + trx_num;
}
}
var StatsDExpects expect := {
{ name := "TTCN3.bsc.0.num_bts.oml_connected", mtype := "g", min := expect_num_bts_connected, max := NUM_BTS_CFG },
{ name := "TTCN3.bsc.0.num_bts.all_trx_rsl_connected", mtype := "g", min := expect_num_bts_connected, max := expect_num_bts_connected },
{ name := "TTCN3.bsc.0.num_bts.total", mtype := "g", min := NUM_BTS_CFG, max := NUM_BTS_CFG },
{ name := "TTCN3.bsc.0.num_trx.rsl_connected", mtype := "g", min := num_trx_connected, max := num_trx_connected },
{ name := "TTCN3.bsc.0.num_trx.total", mtype := "g", min := num_trx_total, max := num_trx_total }
};
g_pars := f_gen_test_hdlr_pars();
f_statsd_expect(expect);
}
private function f_tc_stat_num_bts_connected_test_ct(void_fn tc_fn, integer nr_bts) runs on test_CT {
var integer num_trx_connected := 0;
var integer num_trx_total := 0;
var MSC_ConnHdlr vc_conn;
f_init(nr_bts := nr_bts, handler_mode := true, nr_msc := 1);
f_sleep(1.0);
vc_conn := f_start_handler(tc_fn);
vc_conn.done;
for (var integer i := 0; i < lengthof(c_BtsParams); i := i + 1) {
var integer trx_num := c_BtsParams[i].trx_num;
num_trx_total := num_trx_total + trx_num;
if (i < nr_bts) {
num_trx_connected := num_trx_connected + trx_num;
}
}
/* Also verify stat exposed on CTRL interface */
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_bts:all_trx_rsl_connected", int2str(nr_bts));
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_bts:total", int2str(NUM_BTS_CFG));
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_trx:rsl_connected", int2str(num_trx_connected));
f_ctrl_get_exp(IPA_CTRL, "stat_item.last.bsc.0.num_trx:total", int2str(num_trx_total));
/* Verify rf_states exposed on CTRL interface */
var charstring expect_net_rf_states := "";
for (var integer i := 0; i < NUM_BTS_CFG; i := i + 1) {
var charstring expect_bts_rf_states := "";
for (var integer j := 0; j < c_BtsParams[i].trx_num; j := j + 1) {
expect_bts_rf_states := expect_bts_rf_states &
int2str(i) & "," & int2str(j) & ",";
if (i < NUM_BTS) {
/* In these tests, OML for the first NUM_BTS are always connected via osmo-bts-omldummy */
expect_bts_rf_states := expect_bts_rf_states & "operational,unlocked,";
} else {
/* For remaining i < NUM_BTS_CFG, OML is not connected, i.e. inoperational */
expect_bts_rf_states := expect_bts_rf_states & "inoperational,locked,";
}
/* The RF policy is still global in osmo-bsc, i.e. always "on" */
expect_bts_rf_states := expect_bts_rf_states & "on,";
if (i < nr_bts) {
/* For BTS where RSL is connected, the RSL state will be "up" */
expect_bts_rf_states := expect_bts_rf_states & "rsl-up;";
} else {
expect_bts_rf_states := expect_bts_rf_states & "rsl-down;";
}
}
f_ctrl_get_exp(IPA_CTRL, "bts." & int2str(i) & ".rf_states", expect_bts_rf_states);
expect_net_rf_states := expect_net_rf_states & expect_bts_rf_states;
}
f_ctrl_get_exp(IPA_CTRL, "rf_states", expect_net_rf_states);
f_shutdown_helper();
}
/* Verify that when 1 BTS is connected, that num_{bts,trx}:*_connected reports 1. */
private function f_tc_stat_num_bts_connected_1(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_bts_connected_msc_connhdlr(1);
}
testcase TC_stat_num_bts_connected_1() runs on test_CT {
f_tc_stat_num_bts_connected_test_ct(refers(f_tc_stat_num_bts_connected_1), 1);
}
/* Verify that when 2 BTS is connected, that num_{bts,trx}:*_connected reports 2. */
private function f_tc_stat_num_bts_connected_2(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_bts_connected_msc_connhdlr(2);
}
testcase TC_stat_num_bts_connected_2() runs on test_CT {
f_tc_stat_num_bts_connected_test_ct(refers(f_tc_stat_num_bts_connected_2), 2);
}
/* Verify that when 3 BTS is connected, that num_{bts,trx}:*_connected reports 3. */
private function f_tc_stat_num_bts_connected_3(charstring id) runs on MSC_ConnHdlr {
f_tc_stat_num_bts_connected_msc_connhdlr(3);
}
testcase TC_stat_num_bts_connected_3() runs on test_CT {
f_tc_stat_num_bts_connected_test_ct(refers(f_tc_stat_num_bts_connected_3), 3);
}
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][0].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(tr_RSL_PAGING_CMD(mi), idx := {bts_ids[i], 0});
/* 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][0].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][0].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();
}
/* Send paging response containing invalid (wrongly formatted) MobileIdentity IE. */
testcase TC_paging_imsi_nochan_ci_resp_invalid_mi() runs on test_CT {
var template BSSMAP_FIELD_CellIdentificationList cid_list;
var BSSAP_N_CONNECT_ind rx_c_ind;
var DchanTuple dt;
var octetstring rr_pag_resp := '06270003535992617965720000'O;
/* { 06 27 } is { GSM48_PDISC_RR, GSM48_MT_RR_PAG_RESP }
* see 3GPP TS 44.018, table 9.1.25.1
* { 00 } or { 01 } is CKSN + Spare Half Octet, not important
* { 03 53 59 92 } is Mobile Station Classmark
* { 61 79 65 72 00 00 } is the invalid Mobile Identity IE (3GPP TS 24.008, 10.5.1.4),
* Length is 0x61 (97 in decimal).
*/
cid_list := { cIl_CI := { ts_BSSMAP_CI_CI(cid.ci) } };
f_pageing_helper('001010000000008'H, cid_list, { 0 });
/* Send CHAN RQD and wait for allocation; acknowledge it */
dt.rsl_chan_nr := f_chreq_act_ack();
dt.idx := {0, 0};
/* Send unsolicited Paging response (no matching Paging CMD stored in BSC) */
f_ipa_tx(ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), rr_pag_resp));
/* Expevct a CR with a matching Paging response on the A-Interface */
timer T := 5.0;
T.start;
alt {
[] BSSAP.receive(tr_BSSAP_CONNECT_ind(?, ?, tr_BSSMAP_ComplL3(rr_pag_resp))) -> value rx_c_ind {
setverdict(pass);
dt.sccp_conn_id := rx_c_ind.connectionId;
BSSAP.send(ts_BSSAP_CONNECT_res(dt.sccp_conn_id));
}
[] BSSAP.receive {
setverdict(fail, "Received unexpected message on A-Interface!");
}
[] T.timeout {
setverdict(fail, "Received nothing on A-Interface!");
}
}
f_perform_clear_test_ct(dt);
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(ts_RSL_PAGING_LOAD_IND(0));
f_sleep(0.2);
IPA_RSL[0][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][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(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_bsc;
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");
paging_expired_bsc := f_ctrl_get_ratectr_abs(IPA_CTRL, "bsc", 0, "paging:expired");
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);
f_ctrl_get_exp_ratectr_abs(IPA_CTRL, "bsc", 0, "paging:expired", paging_expired_bsc+1);
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_cn)));
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_cn))) { }
[] 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][0].clear;
}
/* Wait for 3 seconds if any more PAGING CMD are received on RSL */
T.start;
alt {
[] IPA_RSL[0][0].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) {
setverdict(fail, "Received PAGING after A-RESET");
mtc.stop;
}
[] IPA_RSL[1][0].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?))) {
setverdict(fail, "Received PAGING after A-RESET");
mtc.stop;
}
[] IPA_RSL[2][0].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(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();
dt.idx := {0, 0};
/* Send unsolicited Paging response (no matching Paging CMD stored in BSC) */
f_ipa_tx(ts_RSL_EST_IND(dt.rsl_chan_nr, valueof(ts_RslLinkID_DCCH(0)), rr_pag_resp));
/* 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))) -> value rx_c_ind {
setverdict(pass);
dt.sccp_conn_id := rx_c_ind.connectionId;
BSSAP.send(ts_BSSAP_CONNECT_res(dt.sccp_conn_id));
}
[] BSSAP.receive {
setverdict(fail, "Received unexpected message on A-Interface!");
}
[] T.timeout {
setverdict(fail, "Received nothing on A-Interface!");
}
}
f_perform_clear_test_ct(dt);
f_shutdown_helper();
}
/* Verify BSC can schedule N paging requests under one minute if BTS buffer is good enough */
function f_TC_paging_Nreq(integer num_subscribers, boolean send_pag_load_ind) runs on test_CT {
var ASP_RSL_Unitdata rx_rsl_ud;
var Hexstrings imsis := {};
var Booleans rx_paging_done := {};
var integer rx_paging_num := 0;
var integer i;
timer T_rx := 60.0;
timer T_load_ind := 1.0;
for (i := 0; i < num_subscribers; i := i + 1) {
imsis := imsis & {f_gen_imsi(i)};
rx_paging_done := rx_paging_done & { false };
}
f_init(1, guard_timeout := 100.0);
/* Clear the queue, it might still contain stuff like BCCH FILLING */
IPA_RSL[0][0].clear;
if (send_pag_load_ind) {
/* Tell there's plenty of space at the BTS (UINT16_MAX): */
f_ipa_tx(ts_RSL_PAGING_LOAD_IND(65535));
}
for (i := 0; i < num_subscribers; i := i + 1) {
/* Page on LAC-CI of BTS0: */
BSSAP.send(ts_BSSAP_UNITDATA_req(g_bssap[0].sccp_addr_peer, g_bssap[0].sccp_addr_own,
ts_BSSMAP_Paging(imsis[i], valueof(ts_BSSMAP_CIL_LAC_CI({ts_BSSMAP_CI_LAC_CI(1, 0)})),
omit, omit)));
}
T_rx.start;
T_load_ind.start;
alt {
[] IPA_RSL[0][0].receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?), IPAC_PROTO_RSL_TRX0)) -> value rx_rsl_ud {
var hexstring imsi := rx_rsl_ud.rsl.ies[2].body.ms_identity.mobileIdentityV.oddEvenInd_identity.imsi.digits;
var hexstring imsi_suffix := substr(imsi, lengthof(imsi)-6, 6);
var charstring imsi_str := hex2str(imsi_suffix);
var integer imsi_idx := str2int(imsi_str);
if (rx_paging_done[imsi_idx] == false) {
rx_paging_done[imsi_idx] := true;
rx_paging_num := rx_paging_num + 1;
} else {
setverdict(fail, "Retrans of ", imsi_str, " happened before Rx initial trans for all reqs. rx_paging_num=", rx_paging_num);
mtc.stop;
}
if (rx_paging_num < num_subscribers) {
repeat;
}
}
[] IPA_RSL[0][0].receive { repeat; }
[] T_load_ind.timeout {
log("[CCH Load Ind timer] received paging requests so far: ", rx_paging_num);
if (send_pag_load_ind) {
f_ipa_tx(ts_RSL_PAGING_LOAD_IND(40));
}
T_load_ind.start;
repeat;
}
[] T_rx.timeout {
setverdict(fail, "Timeout expecting paging requests, so far ", rx_paging_num);
mtc.stop;
}
}
/* Drop OML connection to have all paging requests flushed: */
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_shutdown_helper();
}
/* Verify BSC can schedule 500 paging requests under one minute if BTS buffer is good enough */
testcase TC_paging_500req() runs on test_CT {
f_TC_paging_Nreq(500, true);
}
/* Same as TC_paging_500req, but without sending CCCH Load Indication, which
* means BTS is always under CCH Load Threshold, aka capable of sending tons of requests.
* Since No CCCH Load Ind, BSC uses a conservative estimation of BTS load, which
* for current config yields ~8req/sec, so 480req/min maximum. */
testcase TC_paging_450req_no_paging_load_ind() runs on test_CT {
f_TC_paging_Nreq(450, false);
}
/* 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");
f_ipa_rsl_stop(bts[0][0].rsl);
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 {
var IPA_Client client;
timer T := 10.0;
client.id := "IPA-BTS0-TRX0-RSL";
client.vc_IPA := IPA_Emulation_CT.create(client.id & "-IPA") alive;
client.ccm_pars := c_IPA_default_ccm_pars;
client.ccm_pars.name := "Osmocom TTCN-3 BTS Simulator";
client.ccm_pars.unit_id := "99/0/0"; /* value which is unknown at BTS */
f_ipa_ctrl_start_client(mp_bsc_ip, mp_bsc_ctrl_port);
f_init_mgcp(0, "VirtMGW");
/* start RSL/OML connection (XXX re-uses RSL port/protocol definitions for OML) */
map(client.vc_IPA:IPA_PORT, system:IPA);
connect(client.vc_IPA:IPA_RSL_PORT, self:IPA_RSL[0][0]);
client.vc_IPA.start(IPA_Emulation.main_client(mp_bsc_ip, mp_bsc_ipa_port, "", 10000, client.ccm_pars));