osmo-ttcn3-hacks/bts/BTS_Tests_LAPDm.ttcn

1494 lines
51 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module BTS_Tests_LAPDm {
import from GSM_Types all;
import from Osmocom_Types all;
import from LAPDm_RAW_PT all;
import from LAPDm_Types all;
import from RSL_Types all;
import from BTS_Tests all;
import from GSM_RR_Types all;
import from Misc_Helpers all;
/* test that use exclusively only LAPDm over L1CTL */
type component lapdm_test_CT {
port LAPDm_PT LAPDM;
var lapdm_CT lapdm_component;
};
/* contrary to BTS_Tests.ttcn, we use LAPDm_PT here, a convenience wrapper
* around L1CTL to perform encode/decode of abstract LAPDm frames */
/*********************************************************************************
* Test using only L1CTL/LAPDm
*********************************************************************************/
function f_lapdm_init() runs on lapdm_test_CT {
/* create the LAPDm component */
lapdm_component := lapdm_CT.create;
/* connect our own LAPDM port to the LAPDM Service Provider of the LAPDm component */
connect(self:LAPDM, lapdm_component:LAPDM_SP);
/* connect the LAPDm compoent's lower-side port to the system L1CTL port (which is internally
* connected to the Unix Domain Socket test port */
map(lapdm_component:L1CTL, system:L1CTL);
/* start the LAPDm parallel component calling it's local function LAPDmStart */
lapdm_component.start(LAPDmStart());
}
function f_lapdm_exit() runs on lapdm_test_CT {
lapdm_component.stop;
lapdm_component.done;
unmap(lapdm_component:L1CTL, system:L1CTL);
}
/* master function switching to a dedicated radio channel */
function f_switch_dcch() runs on ConnHdlr {
var TrxParsItem trx_pars := mp_trx_pars[g_pars.trx_nr];
var GsmBandArfcn arfcn := valueof(ts_GsmBandArfcn(trx_pars.arfcn));
var BCCH_tune_req tune_req := { arfcn := arfcn, combined_ccch := true };
var DCCH_switch_req sw_req;
/* Craft channel description (with or without frequency hopping parameters) */
if (g_pars.fhp.enabled) {
sw_req.chan_desc := valueof(ts_ChanDescH1(g_pars.chan_nr,
g_pars.fhp.maio_hsn,
g_pars.tsc));
sw_req.ma := g_pars.fhp.ma;
} else {
sw_req.chan_desc := valueof(ts_ChanDescH0(g_pars.chan_nr,
trx_pars.arfcn,
g_pars.tsc));
sw_req.ma := omit;
}
LAPDM.send(tune_req);
LAPDM.send(sw_req);
LAPDM.receive(DCCH_switch_res:?);
}
/* helper function releasing dedicated radio channel physically (no Um signaling!) */
function f_release_dcch() runs on lapdm_test_CT {
var DCCH_release_req rel_req := {};
LAPDM.send(rel_req);
}
template LAPDm_ph_data t_PH_DATA(template GsmSapi sapi, template boolean sacch, template LapdmFrame frame) := {
sacch := sacch,
sapi := sapi,
lapdm := frame
}
function f_test_sabm_results_in_ua(uint8_t sapi, boolean use_sacch, octetstring payload) runs on ConnHdlr return boolean {
var LAPDm_ph_data phd;
var boolean result := false;
timer T := 5.0;
LAPDM.send(t_PH_DATA(sapi, use_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=false, l3:=payload)));
T.start
alt {
[] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=false, l3:=payload))) { result := true; }
[] RSL.receive {repeat;}
[] LAPDM.receive { repeat; }
[] T.timeout { }
}
LAPDM.send(t_PH_DATA(sapi, use_sacch, ts_LAPDm_RR(sapi, c_r:=cr_MT_CMD, p:=false, nr:=0)));
return result;
}
function f_TC_sabm_ua_dcch_sapi0(charstring id) runs on ConnHdlr {
fp_common_init();
RSL.clear;
LAPDM.clear;
var default d := activate(as_ignore_background());
if (not f_test_sabm_results_in_ua(0, false, 'FEFE'O)) {
setverdict(fail);
}
setverdict(pass);
deactivate(d);
fp_common_fini();
}
function f_TC_sabm_ua_dcch_sapi0_nopayload(charstring id) runs on ConnHdlr {
fp_common_init();
RSL.clear;
LAPDM.clear;
var default d := activate(as_ignore_background());
if (f_test_sabm_results_in_ua(0, false, ''O)) {
setverdict(fail, "Initial SABM/UA must contain L3 payload but BTS accepts without");
}
setverdict(pass);
deactivate(d);
fp_common_fini();
}
function f_TC_sabm_ua_dcch_sapi3(charstring id) runs on ConnHdlr {
fp_common_init();
RSL.clear;
LAPDM.clear;
var default d := activate(as_ignore_background());
if (f_test_sabm_results_in_ua(3, false, 'FEFE'O)) {
setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=3");
}
setverdict(pass);
deactivate(d);
fp_common_fini();
}
function f_TC_sabm_ua_dcch_sapi4(charstring id) runs on ConnHdlr {
fp_common_init();
RSL.clear;
LAPDM.clear;
var default d := activate(as_ignore_background());
if (f_test_sabm_results_in_ua(4, false, 'FEFE'O)) {
setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=4");
}
setverdict(pass);
deactivate(d);
fp_common_fini();
}
testcase TC_sabm_ua_dcch_sapi0() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_ua_dcch_sapi0));
}
testcase TC_sabm_ua_dcch_sapi0_nopayload() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_ua_dcch_sapi0_nopayload));
}
testcase TC_sabm_ua_dcch_sapi3() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_ua_dcch_sapi3));
}
testcase TC_sabm_ua_dcch_sapi4() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_ua_dcch_sapi4));
}
function f_TC_sabm_contention(charstring id) runs on ConnHdlr {
var LAPDm_ph_data phd;
const octetstring payload := '0102030405'O;
const GsmSapi sapi := 0;
const boolean use_sacch := false;
timer T := 5.0;
fp_common_init();
RSL.clear;
LAPDM.clear;
/* first frame is our real SABM */
LAPDM.send(t_PH_DATA(sapi, use_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=false, l3:=payload)));
/* second frame is a SABM with different payload, which BTS has to ignore according to 8.4.1.4 */
LAPDM.send(t_PH_DATA(sapi, use_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=false, l3:='ABCDEF'O)));
T.start
alt {
[] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=false, l3:=payload))) { setverdict(pass); repeat; }
[] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=false, l3:=?))) {
setverdict(fail, "Second SABM was responded to during contention resolution");
}
[] RSL.receive {repeat;}
[] LAPDM.receive { repeat };
[] T.timeout { }
}
fp_common_fini();
}
testcase TC_sabm_contention() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_contention));
}
/* we test that a re-transmitted SABM with identical payload will result in the retransmission of a
* UA. This is required during the contention resolution procedure as specified in 8.4.1.4 */
function f_TC_sabm_retransmit(charstring id) runs on ConnHdlr {
const octetstring payload := '00FEFEDEADBEEF'O;
fp_common_init();
RSL.clear;
LAPDM.clear;
var default d := activate(as_ignore_background());
if (not f_test_sabm_results_in_ua(0, false, payload)) {
setverdict(fail);
}
if (not f_test_sabm_results_in_ua(0, false, payload)) {
setverdict(fail);
}
setverdict(pass);
deactivate(d);
fp_common_fini();
}
testcase TC_sabm_retransmit() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_retransmit));
}
/*********************************************************************************
* Test using both L1CTL/LAPDm and RSL
*********************************************************************************/
private function fp_common_init() runs on ConnHdlr
{
/* undo what f_start_handler is doing and pull LAPDm_CT into the loop */
unmap(self:L1CTL, system:L1CTL);
f_lapdm_init();
/* Obtain frequency hopping parameters for a given timeslot */
if (mp_freq_hop_enabled and mp_transceiver_num > 1) {
f_resolve_fh_params(g_pars.fhp, g_pars.chan_nr.tn);
}
/* activate the channel on the BTS side */
f_rsl_chan_act(g_pars.chan_mode, false, {});
/* activate the channel on the MS side */
f_switch_dcch();
}
private function fp_common_fini() runs on ConnHdlr
{
f_release_dcch();
f_rsl_chan_deact();
f_lapdm_exit();
}
/* Mobile-Originated LAPDm establishment on given Link ID */
private function f_establish_mo(RslLinkId link_id) runs on ConnHdlr
{
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
var octetstring l3_mo := f_rnd_octstring(5);
/* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
if (is_sacch) {
/* no payload permitted, as this is not contention resolution */
l3_mo := ''O;
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, link_id));
} else {
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
}
/* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
}
/* Verify that the BTS is re-transmitting SABM messages after T200 timeout, inspired
by 3GPP TS 51.010-1 25.2.1.1.2.1 + 25.2.1.2.4 */
private function f_TC_sabm_retransmit_bts(charstring id) runs on ConnHdlr {
const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
fp_common_init();
LAPDM.clear;
RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
timer T := 8.0;
var integer sabm_received := 0;
T.start;
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
sabm_received := sabm_received + 1;
repeat;
}
[] LAPDM.receive { repeat; }
[] T.timeout { }
}
if (sabm_received == 0) {
setverdict(fail, "No SABM observed at all!");
} else if (sabm_received != 6) {
setverdict(fail, "Incorrect number of SABM re-transmissions of observed: ",
sabm_received);
} else {
setverdict(pass, "Received ", sabm_received, " SABM");
}
fp_common_fini();
}
testcase TC_sabm_retransmit_bts() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_retransmit_bts));
}
type record LapdmNamedFrame {
charstring name,
LapdmFrame lapdm
};
/* Test that the BTS will ignore receipt of frames other than a UA when
* received in response to the SABM frame, inspired from 3GPP TS 51.010-1
* Section 25.2.1.1.2.3 */
private function f_TC_sabm_invalid_resp2(charstring id, LapdmNamedFrame err_frame) runs on ConnHdlr {
var integer sapi := err_frame.lapdm.ab.addr.sapi;
fp_common_init();
/* Establish Request via RSL; Expect SABM on LAPDm side */
LAPDM.clear;
RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O)));
[] LAPDM.receive { repeat; }
}
/* send erroneous response to SABM */
LAPDM.send(t_PH_DATA(0, false, err_frame.lapdm));
/* expect a SABM retransmission of the BTS */
timer T := 3.0;
T.start;
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
setverdict(pass);
}
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
[] LAPDM.receive(t_PH_DATA(0, false, ?)) {
setverdict(fail, "Received unexpected LAPDm frame instead of SABM after sending ",
err_frame.name);
}
[] LAPDM.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for SABM retransmission after sending ",
err_frame.name);
}
}
fp_common_fini();
}
private function f_TC_sabm_invalid_resp(charstring id) runs on ConnHdlr {
const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
var LapdmNamedFrame err_frame[3] := {
{ "I", valueof(ts_LAPDm_I(sapi, c_r := cr_MO_CMD, p := true, nr := 0, ns := 0,
l3 := '01020304'O)) },
{ "RR", valueof(ts_LAPDm_RR(sapi, c_r := cr_MO_CMD, p := true, nr := 0)) },
{ "REJ" , valueof(ts_LAPDm_REJ(sapi, c_r := cr_MO_CMD, p := true, nr := 0)) }
};
var integer i;
for (i := 0; i < lengthof(err_frame); i := i+1) {
f_TC_sabm_invalid_resp2(id, err_frame[i])
}
}
testcase TC_sabm_invalid_resp() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_invalid_resp));
}
/* Test that the BTS will not re-transmit SABM frames after receiving a DM response,
* inspired from 3GPP TS 51.010-1 Section 25.2.1.1.3 */
private function f_TC_sabm_dm(charstring id) runs on ConnHdlr {
const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
fp_common_init();
/* Establish Request via RSL; Expect SABM on LAPDm side */
LAPDM.clear;
RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O)));
[] LAPDM.receive { repeat; }
}
/* send DM response to SABM */
RSL.clear;
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_DM(sapi, c_r:=cr_MO_RSP, f:=true)));
alt {
[] RSL.receive(tr_RSL_REL_IND(g_chan_nr, tr_RslLinkID_DCCH(sapi)));
[] RSL.receive { repeat; }
}
/* expect no SABM retransmission of the BTS */
timer T := 3.0;
T.start;
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
setverdict(fail, "Received unexpected SABM retransmission");
}
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
[] LAPDM.receive(t_PH_DATA(0, false, ?)) {
setverdict(fail, "Received unexpected LAPDm frame");
}
[] LAPDM.receive { repeat; }
[] T.timeout {
setverdict(pass);
}
}
fp_common_fini();
}
testcase TC_sabm_dm() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_dm));
}
/* Test the full LAPDm establishment while simulating the loss of the initial SABM or UA
* frame, requiring the BTS to re-transmit one SABM and then following up all the way to
* dedicated mode; inspired by 3GPP TS 51.010-1 25.2.1.2.2 */
private function f_TC_establish_ign_first_sabm(charstring id) runs on ConnHdlr {
const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
var integer num_sabm := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
timer T := 3.0;
fp_common_init();
/* Establish Request via RSL */
LAPDM.clear;
RSL.send(ts_RSL_EST_REQ(g_chan_nr, link_id));
/* Expect two SABM (retransmit) */
T.start;
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
num_sabm := num_sabm + 1;
if (num_sabm < 2) {
repeat;
}
}
[] LAPDM.receive { repeat; }
}
/* send UA response to SABM */
RSL.clear;
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_UA(sapi, c_r:=cr_MO_RSP, f:=true, l3:=''O)));
alt {
[] RSL.receive(tr_RSL_EST_CONF(g_chan_nr, link_id));
[] RSL.receive { repeat; }
}
/* Send I frame from BTS to MS */
var octetstring l3 := f_rnd_octstring(10);
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3));
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=0, ns:=0, l3:=l3)));
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
[] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
[] LAPDM.receive { setverdict(fail, "Unexpected LAPDm received"); }
}
/* Send RR frame in response */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=false, nr:=1)));
/* expect idle UI Frame from BTS */
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) {
setverdict(pass);
}
[] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
[] LAPDM.receive { setverdict(fail, "Unexpected LAPDm received"); }
}
fp_common_fini();
}
testcase TC_establish_ign_first_sabm() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_establish_ign_first_sabm));
}
/* ignore all SACCH frames */
private altstep as_lapdm_acch() runs on ConnHdlr {
[] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
}
/* ignore all DCCH frames */
private altstep as_lapdm_dcch() runs on ConnHdlr {
[] LAPDM.receive(t_PH_DATA(0, false, ?)) { repeat; }
}
/* ignore all LAPDm idle frames (UI) */
private altstep as_lapdm_idle() runs on ConnHdlr {
[] LAPDM.receive(t_PH_DATA(0, ?, tr_LAPDm_UI(?, ?, ''O))) { repeat; }
}
/* ignore all measurement reports */
private altstep as_rsl_meas_rep() runs on ConnHdlr {
[] RSL.receive(tr_RSL_MEAS_RES(g_chan_nr)) { repeat; }
}
/* fail if we receive an RSL ERROR IND */
private altstep as_rsl_fail_err() runs on ConnHdlr {
var RSL_Message rx_rsl;
[] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, ?, ?)) {
setverdict(fail, "Received RSL ERROR IND ", rx_rsl);
}
}
/* all of the above */
private altstep as_ignore_background(boolean want_dcch := true, boolean ignore_rsl_errors := false) runs on ConnHdlr {
[want_dcch] as_lapdm_acch();
[not want_dcch] as_lapdm_dcch();
[] as_lapdm_idle();
[] as_rsl_meas_rep();
[not ignore_rsl_errors] as_rsl_fail_err();
[ignore_rsl_errors] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, ?, ?)) { repeat;}
}
/* Test the operation of Layer 2 sequence numbering.
* dedicated mode; inspired by 3GPP TS 51.010-1 25.2.2.1 */
private function f_TC_iframe_seq_and_ack(charstring id) runs on ConnHdlr {
const integer sapi := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
var octetstring l3 := f_rnd_octstring(18);
var default d;
timer T := 3.0;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background());
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
var integer last_ns_rx := 0;
for (var integer i := 0; i < 10; i := i+1) {
var octetstring l3_mo := f_rnd_octstring(12);
var octetstring l3_mt := f_rnd_octstring(12);
var LAPDm_ph_data pd;
log("Starting iteration ", i);
/* MT I frame */
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
alt {
/* SAPI = 0, R = 1, F = 0, M = 0, L = 0. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false,
nr:=i mod 8))) {
log("Ignoring RR in iteration ", i);
repeat;
}
/* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=i mod 8, ns:=i mod 8,
l3:=l3_mt))) -> value pd {
last_ns_rx := pd.lapdm.ab.ctrl.i.n_s;
}
}
/* respond with MO I-frame: SAPI = 0, C = 0, P = 0, M = 0, 0 <= L <= N201. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=(last_ns_rx+1)mod 8,
ns:=i mod 8, l3 := l3_mo)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
}
log("Completed iteration");
deactivate(d);
fp_common_fini();
}
testcase TC_iframe_seq_and_ack() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_iframe_seq_and_ack));
}
/* To test that the BTS is able to respond to I frames whilst in the timer recovery state.
* Inspired by 3GPP TS 51.010-1 25.2.2.2 */
/*
1) The BTS is brought into the multiple frame established state
2) The MS sends an L3 Request asking for IMEI to the MS.
3) The BTS shall respond with a RR frame though this may be incorporated with
the L3 Response I frame. The MS does not respond to the I frame.
4) The BTS shall wait for expiry of timer T200 and then repeat the I frame but
with the P bit set to 1.
5) The MS then sends a valid L3 Request I frame asking for IMEI which
does not acknowledge receipt of the I frame from the BTS.
On the FACCH the BTS may send an RR frame acknowledging the I frame.
6) The BTS shall repeat the I frame, this frame will acknowledge receipt of
the second I frame from the MS.
7) The MS then acknowledges receipt of the MS I frame by sending a RR frame.
8) The BTS shall send the next I frame. The MS acknowledges this I frame.
*/
private function f_TC_iframe_timer_recovery(charstring id) runs on ConnHdlr {
const integer sapi := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
var default d;
timer T := 3.0;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background());
RSL.clear;
LAPDM.clear;
var octetstring l3_mo := f_rnd_octstring(12);
var octetstring l3_mt := f_rnd_octstring(12);
/* 1) The BTS is brought into the multiple frame established state */
/* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
/* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
/* 2) The MS sends an L3 Request to the BTS */
l3_mo := f_rnd_octstring(18);
/* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201, N(S) = 0, N(R) = 0. * */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=0, ns:=0, l3:=l3_mo)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
/* 3) The BTS shall respond with a RR frame though this may be incorporated with
the L3 Response I frame. The MS does not respond to the I frame. */
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
alt {
/* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 1. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1))) {
repeat;
}
/* SAPI = 0, C = 0, P = 0, M = 0, 0 ≤ L ≤ N201, N(R) = 1, N(S) = 0 */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=1, ns:=0, l3:=l3_mt)));
}
/* 4) The BTS shall wait for expiry of timer T200 and then repeat the I frame but
with the P bit set to 1. */
/* SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201, N(R) = 1, N(S) = 0. * */
LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=true,
nr:=1, ns:=0, l3:=l3_mt)));
/* 5) The MS then sends a valid L3 Request I frame asking for IMEI which
does not acknowledge receipt of the I frame from the BTS. */
/* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201, N(S) = 1, N(R) = 0. * */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=0, ns:=1, l3 := l3_mo)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
alt {
/* On the FACCH the BTS may send an RR frame acknowledging the I frame. */
/* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 2. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=2))) {
repeat;
}
/* 6) The BTS shall repeat the I frame, this frame will acknowledge
receipt of the second I frame from the MS. */
/* SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201, N(R) = 2, N(S) = 0. * */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=true,
nr:=2, ns:=0, l3:=l3_mt)));
}
/* 7) The MS then acknowledges receipt of the BTS I frame by sending a RR * frame. */
/* SAPI = 0, R = 0, F = 1, 0, M = 0, L = 0, N(R) = 1 */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=true, nr:=1)));
/* 8) The BTS shall send the next I frame. The MS acknowledges this I frame. */
l3_mt := f_rnd_octstring(16);
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
/* SAPI = 0, C = 0, P = 0, M = 0, 0 ≤ L ≤ N201, N(R) = 2, N(S) = 1 */
LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=2, ns:=1, l3:=l3_mt)));
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=true, nr:=2)));
deactivate(d);
fp_common_fini();
}
testcase TC_iframe_timer_recovery() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_iframe_timer_recovery));
}
/* 25.2.6.1 ns sequence error
sends wrong N(S), expects REJ, sends wrong N(S) with P=1, expects REJ with F=1 */
private function f_TC_ns_seq_error(charstring id) runs on ConnHdlr {
const integer sapi := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
var default d;
timer T := 3.0;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(ignore_rsl_errors := true));
RSL.clear;
LAPDM.clear;
var octetstring l3_mo := f_rnd_octstring(12);
var octetstring l3_mt := f_rnd_octstring(12);
/* 1) The BTS is brought into the multiple frame established state */
/* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
/* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
/* 2) The MS sends an L3 Request to the BTS */
l3_mo := f_rnd_octstring(18);
/* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201, N(S) = 0, N(R) = 0. * */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=0, ns:=0, l3:=l3_mo)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
/* 3) The BTS shall respond with a RR frame though this may be incorporated with
the L3 Response I frame. */
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
alt {
/* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 1. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1))) {
repeat;
}
/* SAPI = 0, C = 0, P = 0, M = 0, 0 ≤ L ≤ N201, N(R) = 1, N(S) = 0 */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=1, ns:=0, l3:=l3_mt)));
}
/* 4) The MS shall then send an I frame containing Identity Request with incorrect N(S)
but correctly acknowledging the MS's I frame; P bit set to zero. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=1, ns:=0, l3:=l3_mo)));
/* no rsl data ind due to wrong ns */
/* The BTS shall send a REJ frame. */
timer T1 := 2.0;
T1.start;
alt{
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_REJ(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1)));
[] T1.timeout{ setverdict(fail, "Missing first REJ")}
}
f_sleep(2.0); // T200
/* The MS shall, after T200, send another I frame with incorrect N(S), P bit set to 1 this time. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=true,
nr:=1, ns:=0, l3:=l3_mo)));
/* The BTS shall respond with a REJ, F bit set to 1. */
T1.start;
alt{
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_REJ(sapi, c_r:=cr_MT_RSP, p:=true, nr:=1)));
[] T1.timeout{ setverdict(fail, "Missing second REJ")}
}
deactivate(d);
fp_common_fini();
setverdict(pass);
}
testcase TC_ns_seq_error() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_ns_seq_error));
}
/* 25.2.6.2 nr sequence error */
private function f_TC_nr_seq_error(charstring id) runs on ConnHdlr {
const integer sapi := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
var default d;
var integer n201 := 20;
if (link_id.c == SACCH) {
n201 := 18;
}
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(ignore_rsl_errors := true));
RSL.clear;
LAPDM.clear;
var octetstring l3_mo := f_rnd_octstring(12);
var octetstring l3_mt := f_rnd_octstring(12);
/* 1) The BTS is brought into the multiple frame established state */
/* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
/* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
/* 2) The MS shall send an I frame containing an information field of length N201 and an
incorrect receive sequence number. */
l3_mo := f_rnd_octstring(n201);
/* SAPI = 0, C = 1, P = 0, M = 0, L = N201, N(S) = 0, N(R) = 1. * */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
nr:=1, ns:=0, l3:=l3_mo)));
/* The BTS may: a) send a DISC frame within N200×T200; */
/* DISC SAPI = 0, C = 0, P = 1, M = 0, L = 0. */
timer T1 := 2.0;
T1.start;
alt{
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_DISC(sapi, c_r:=cr_MT_CMD, p:=true)));
[] T1.timeout{ setverdict(fail, "Missing DISC from BTS")}
}
/* a) the MS shall respond with a UA frame.
SAPI = 0, R = 0, F = 1, M = 0, L = 0. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_UA(sapi, c_r:=cr_MO_CMD, f:=true, l3:=l3_mo)));
deactivate(d);
fp_common_fini();
setverdict(pass);
}
testcase TC_nr_seq_error() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_nr_seq_error));
}
private function f_TC_rec_invalid_frame_txrx(integer sapi, LapdmFrame x, integer line_nr) runs on ConnHdlr {
LAPDM.send(t_PH_DATA(0, false, x));
f_sleep(2.0); // T200
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=true, nr:=0)));
timer T1 := 2.0;
T1.start;
alt {
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=true , nr := 0))) { T1.stop; }
[] T1.timeout{ Misc_Helpers.f_shutdown(__BFILE__, line_nr, fail, "Missing LAPDm_RR RSP"); }
}
}
/* 25.2.7 Test on receipt of invalid frames
sends a bunch of different invalid frames, expects the BTS to ignore all of them */
private function f_TC_rec_invalid_frame(charstring id) runs on ConnHdlr {
const integer sapi := 0;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
var default d;
timer T := 3.0;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(ignore_rsl_errors := true));
RSL.clear;
LAPDM.clear;
var octetstring l3_mo := f_rnd_octstring(12);
var octetstring l3_mt := f_rnd_octstring(12);
/* 1) The BTS is brought into the multiple frame established state */
/* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
T.start
alt {
[] RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo)) {}
[] T.timeout{ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Missing RSL EST IND"); }
}
alt {
/* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo))) { T.stop; }
[] T.timeout{ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Missing LAPDm UA RSP"); }
}
/* 1: One RR frame: SAPI = 0, R = 0, F = 0, M = 0, L > 0, N(R) = 1.
RR frame with the Length indicator greater than zero and a faulty N(R); */
var LapdmFrame x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.payload := f_rnd_octstring(5);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 3: One REJ frame: SAPI = 0, R = 0, F = 0, M = 0, L = 0, N(R) = 1, EA = 0.
EJ frame with the EA bit set to zero and a faulty N(R); */
x:= valueof(ts_LAPDm_REJ(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.addr.ea := false;
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 4: One SABM frame: SAPI = 0, C = 1, P = 1, M = 0, L = 0, EL = 0.
SABM frame with the EL bit set to zero; */
l3_mo := ''O;
x:= valueof(ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo));
x.ab.el := 0;
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 5: One DM frame: SAPI = 0, R = 0, F = 1, M = 0, L > 0.
DM frame with the Length indicator greater than zero;*/
x:= valueof(ts_LAPDm_DM(sapi, c_r:=cr_MO_CMD, f:=true));
x.ab.payload := f_rnd_octstring(5);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 6: One DISC frame: SAPI = 0, C = 1, P = 1, M = 1, L = 0.
DISC frame with the M bit set to 1; */
x:= valueof(ts_LAPDm_DISC(sapi, c_r:=cr_MO_CMD, p:=true));
x.ab.m := true;
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 7: One UA frame: SAPI = 0, R = 0, F = 0, M = 0, L = 0, EA = 0.
UA frame with the EA bit set to zero*/
x:= valueof(ts_LAPDm_UA(sapi, c_r:=cr_MO_CMD, f:=false, l3:=''O));
x.ab.addr.ea := false;
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 8: One I frame: SAPI = 0, C = 1, P = 0, M = 0, L > N201, N(R) = 0, N(S) = 6.
I frame with the Length indicator greater than N201;*/
x:= valueof(ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=true, nr := 0, ns := 6, l3 := f_rnd_octstring(25)))
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 9: One I frame: SAPI = 0, C = 1, P = 0, M = 1, L < N201, N(R) = 0, N(S) = 7.
I frame with the M bit set to 1 and the Length indicator less than N201;*/
x:= valueof(ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=true, nr := 0, ns := 7, l3 := f_rnd_octstring(5)))
x.ab.m := true;
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 10: One RR frame: SAPI = 0, C = 1, P = 1, M = 0, L = 0, N(R) = 0. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=true, nr:=0));
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* command frames with correct Address and Length indicator field and a non-implemented control field */
/* 12: One command frame with Control Field = xxx1 1101. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('00011101'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 13: One command frame with Control field = xxx1 1011. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('00011011'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 14: One command frame with Control field = xxx1 0111. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('00010001'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 15: One command frame with Control field = 01x1 1111. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('01011111'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 16: One command frame with Control field = 1xx1 1111. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('10011111'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 17: One command frame with Control field = 0011 0011. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('00110011'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
/* 18: One command frame with Control field = 1xx1 0011. */
x:= valueof(ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=false, nr:=1));
x.ab.ctrl.other := bit2int('10010011'B);
f_TC_rec_invalid_frame_txrx(0, x, __LINE__);
deactivate(d);
fp_common_fini();
setverdict(pass);
}
testcase TC_rec_invalid_frame() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.t_guard := 60.0;
f_testmatrix_each_chan(pars, refers(f_TC_rec_invalid_frame));
}
type record LapdmDlConfig {
integer n201,
integer t200
};
type record LapdmDlState {
integer v_s,
integer v_a,
integer v_r
};
template (value) LapdmDlState t_init_LapdmDlState := {
v_s := 0,
v_a := 0,
v_r := 0
}
private function inc_mod8(inout integer v)
{
v := (v + 1) mod 8;
}
private function f_lapdm_transceive_mo(inout LapdmDlState dls, RslLinkId link_id, octetstring l3)
runs on ConnHdlr {
var LAPDm_ph_data pd;
var integer offset := 0;
var integer n201 := 20;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
n201 := 18;
is_sacch := true;
}
while (offset < lengthof(l3)) {
var integer remain_len := lengthof(l3) - offset;
var integer seg_len := remain_len;
if (remain_len > n201) {
seg_len := n201;
}
var octetstring segment := substr(l3, offset, seg_len);
var boolean more;
if (offset + lengthof(segment) < lengthof(l3)) {
more := true;
} else {
more := false;
}
/* send the next segment */
LAPDM.send(t_PH_DATA(0, is_sacch,
ts_LAPDm_I(link_id.sapi, c_r:=cr_MO_CMD, p:=false,
nr:=dls.v_a, ns:=dls.v_s, l3:=segment, m:=more)));
inc_mod8(dls.v_s);
offset := offset + lengthof(segment);
/* wait for it to be acknowledged */
alt {
[] LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(link_id.sapi, c_r:=cr_MT_RSP,
p:=false, nr:=(dls.v_s) mod 8)));
[] as_ignore_background(not is_sacch);
[] LAPDM.receive(t_PH_DATA(0, is_sacch, ?)) -> value pd {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("received unexpected LAPDm ", pd));
}
[] LAPDM.receive(t_PH_DATA(0, ?, ?)) { repeat; }
[offset < lengthof(l3)] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, ?)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "received RSL DATA IND before message complete");
}
}
}
timer T := 1.0;
T.start;
alt {
[] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3)) {
setverdict(pass);
}
[] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, ?)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received RSL DATA IND with wrong payload");
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for RSL DATA IND of de-segmented message");
}
}
}
/* Section 5.8.5 of TS 04.06 */
const integer c_TS0406_MAX_L3_OCTETS := 251;
/* test segmentation and de-segmentation (concatenation) of a large message in uplink
* on specified SAPI/channel */
private function f_TC_segm_concat(charstring id, RslLinkId link_id) runs on ConnHdlr {
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
var default d;
timer T := 3.0;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(not is_sacch));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
var octetstring l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
deactivate(d);
var LapdmDlState dls := valueof(t_init_LapdmDlState);
f_lapdm_transceive_mo(dls, link_id, l3_mo);
fp_common_fini();
}
private function f_TC_segm_concat_dcch(charstring id) runs on ConnHdlr {
f_TC_segm_concat(id, valueof(ts_RslLinkID_DCCH(0)));
}
private function f_TC_segm_concat_sacch(charstring id) runs on ConnHdlr {
f_TC_segm_concat(id, link_id :=valueof(ts_RslLinkID_SACCH(0)));
}
/* test mobile-originated segmentation/de-segmentation on DCCH */
testcase TC_segm_concat_dcch() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_segm_concat_dcch));
}
/* test mobile-originated segmentation/de-segmentation on SACCH */
testcase TC_segm_concat_sacch() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_segm_concat_sacch));
}
/* TS 04.06 Section 5.8.2.1 */
private function f_n200_by_chan_nr(RslChannelNr chan_nr, RslLinkId link_id) return integer {
/* SACCH irrespective of physical channel type */
if (match(link_id, tr_RslLinkID_SACCH(?))) {
return 5;
}
/* DCCH below */
select (chan_nr) {
case (t_RslChanNr_SDCCH4(?, ?)) { return 23; }
case (t_RslChanNr_SDCCH8(?, ?)) { return 23; }
case (t_RslChanNr_Bm(?)) { return 34; }
case (t_RslChanNr_Lm(?, ?)) { return 29; }
}
setverdict(fail, "Unknown chan_nr ", chan_nr, " or link_id ", link_id);
return -1;
}
/* Test if there are exactly N200+1 transmissions of I frames; inspired by 25.2.4.1 */
private function f_TC_t200_n200(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
var integer n200 := f_n200_by_chan_nr(g_chan_nr, link_id);
var integer num_retrans := 0;
timer T := 3.0;
var default d;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
var octetstring l3_mt := f_rnd_octstring(20);
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
/* first transmission, P = 0 */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
nr:=0, ns:=0, l3:=l3_mt)));
deactivate(d);
alt {
/* re-transmission, P = 1 */
[] LAPDM.receive(t_PH_DATA(0, is_sacch,
tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=true, nr:=0, ns:=0, l3:=l3_mt))) {
num_retrans := num_retrans + 1;
if (num_retrans < n200) {
repeat;
} else if (num_retrans == n200) {
T.start; /* wait for some more time if there are more retransmissions */
repeat;
} else {
/* break */
}
}
[] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, ?, ?, ?, ?, ?))) {
setverdict(fail, "Received unexpected I frame");
}
[not is_sacch] as_lapdm_acch();
[is_sacch] as_lapdm_dcch();
[] as_lapdm_idle();
[] as_rsl_meas_rep();
[num_retrans == n200] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, link_id, '01'O)) {
/* break */
}
[] T.timeout {
setverdict(fail, "Missing RSL RLL ERROR INDICATION");
}
}
if (num_retrans == n200) {
setverdict(pass, "Received ", num_retrans, " on channel ", g_chan_nr, " link ", link_id);
} else if (num_retrans < n200) {
setverdict(fail, "Too few retransmissions (", num_retrans, "); N200=", n200,
" on channel ", g_chan_nr, " link ", link_id);
}
fp_common_fini();
}
testcase TC_t200_n200() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_t200_n200));
}
/* Ensure BTS repeats RR frame after retransmitting I frame to emulate RR loss;
Inspired by TS 51.010-1 25.2.4.3 */
private function f_TC_rr_response_frame_loss(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
timer T := 3.0;
var default d;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
var octetstring l3_mo := f_rnd_octstring(10);
/* Send an I frame to the BTS: SAPI = 0, C = 1, P = 0, M = 0, L = 3, N(S) = 0, N(R) = 0 */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false, nr:=0, ns:=0,
l3:=l3_mo)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
/* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 1. */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1)));
/* Re-send I frame: SAPI = 0, C = 1, P = 1, M = 0, L = 3, N(S) = 0, N(R) = 0. */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=true, nr:=0, ns:=0,
l3:=l3_mo)));
T.start;
alt {
/* RR: SAPI = 0, R = 1, F = 1, M = 0, L = 0, N(R) = 1. */
[] LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=true, nr:=1))) {
setverdict(pass);
}
/* REJ: SAPI = 0, R = 1, F = 1, M = 0, L = 0, N(R) = 1. */
[] LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_REJ(sapi, c_r:=cr_MT_RSP, p:=true, nr:=1))) {
setverdict(pass);
}
[] T.timeout {
setverdict(fail, "Timeout waiting for RR or REJ");
}
}
deactivate(d);
fp_common_fini();
}
testcase TC_rr_response_frame_loss() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_rr_response_frame_loss));
}
/* Ensure BTS ignores I frames with wrong C/R bit; Inspired by TS 51.010-1 25.2.5.1 */
private function f_TC_incorrect_cr(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
timer T := 3.0;
var default d;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
var octetstring l3_mo := f_rnd_octstring(10);
/* Send an I frame to the BTS: SAPI = 0, C = 0, P = 1, M = 0, L = 3, N(S) = 0, N(R) = 0 */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_I(sapi, c_r:=cr_MO_RSP, p:=true, nr:=0, ns:=0,
l3:=l3_mo)));
T.start;
alt {
[] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo)) {
setverdict(fail, "BTS didn't ignore I frame with wrong C/R bit");
}
[] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, link_id, '0C'O)) {
repeat;
}
/* ensure BTS still sends idle frames */
[] as_lapdm_idle() {
setverdict(pass, "still sending idle frames");
}
[] T.timeout {}
}
/* Send RR command P=1 */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=true, nr:=0)));
/* The BTS shall respond with a RR response, F bit set to 1. */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=true, nr:=0)));
deactivate(d);
fp_common_fini();
}
testcase TC_incorrect_cr() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_incorrect_cr));
}
/* test that the BTS will take no action when it receives an SABM frame with the C bit set wrong (R)
Inspired by TS 51.010-1 25.2.5.2 */
private function f_TC_sabm_incorrect_c(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
var integer sapi := link_id.sapi;
var boolean is_sacch := false;
if (link_id.c == SACCH) {
is_sacch := true;
}
timer T := 3.0;
var default d;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
/* Send I-frame SAPI = 0, C = 1, P = 0, M = 0, L = 3, N(S) = 0, N(R) = 0 */
var octetstring l3_mo := '010203'O;
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false, nr:=0, ns:=0,
l3:=l3_mo)));
/* Expect RR SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 1 */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1)));
RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
/* Send SABM SAPI = 0, C = 0, P = 1, M = 0, L = 0 */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_RSP, p:=true, l3:=''O)));
/* Expect RSL ERR IND */
RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, link_id, '0C'O));
/* Expect fill frame C = 0, P = 0, M = 0, L = 0 */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_UI(0, ?, ''O)));
/* Send RR command (P=1) SAPI = 0, C = 1, P = 1, M = 0, L = 0, N(R) = 0 */
LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_RR(sapi, c_r:=cr_MO_CMD, p:=true, nr:=0)));
/* Expect RR response (F=1) SAPI = 0, R = 1, F = 1, M = 0, L = 0, N(R) = 1 */
LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=true, nr:=1)));
deactivate(d);
fp_common_fini();
}
testcase TC_sabm_incorrect_c() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_sabm_incorrect_c));
}
/* Test procedure for normal reestablishment, as per:
* OS#4819
* 3GPP TS 44.006 8.4.1.2 "Normal establishment procedure"
* 3GPP TS 44.006 8.4.2.1 "General requirements"
* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment"
* */
private function f_TC_normal_reestablishment(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0))
var octetstring l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
var default d;
timer T := 3.0;
var boolean use_sacch := false;
var boolean received_estind := false;
var boolean received_ua := false;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
deactivate(d);
var LapdmDlState dls := valueof(t_init_LapdmDlState);
f_lapdm_transceive_mo(dls, link_id, l3_mo);
LAPDM.send(t_PH_DATA(link_id.sapi, use_sacch, ts_LAPDm_SABM(link_id.sapi, c_r:=cr_MO_CMD, p:=true, l3:=''O)));
T.start
alt {
[] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_UA(link_id.sapi, cr_MT_RSP, f:=true, l3:=''O))) {
received_ua := true;
if (not received_estind) {
repeat;
}
}
[] RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, link_id)) {
received_estind := true;
if (not received_ua) {
repeat;
}
}
[] RSL.receive { repeat; }
[] LAPDM.receive { repeat; }
[] T.timeout { setverdict(fail, "Timeout waiting for UA"); }
}
/* Test we can still send data afterwards */
l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
dls := valueof(t_init_LapdmDlState);
f_lapdm_transceive_mo(dls, link_id, l3_mo);
fp_common_fini();
}
testcase TC_normal_reestablishment() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_normal_reestablishment));
}
/* Test procedure for normal reestablishment in state LAPD_STATE_TIMER_RECOV (after T200, waiting for Ack), as per:
* OS#4819
* 3GPP TS 44.006 8.4.1.2 "Normal establishment procedure"
* 3GPP TS 44.006 8.4.2.1 "General requirements"
* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment"
* */
private function f_TC_normal_reestablishment_state_unacked(charstring id) runs on ConnHdlr {
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0))
var octetstring l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
var octetstring l3_mt := f_rnd_octstring(20);
var LapdmDlState dls;
var default d;
timer T := 3.0;
var boolean use_sacch := false;
var boolean received_estind := false;
var boolean received_ua := false;
fp_common_init();
/* some common altstep for meas res and other background noise */
d := activate(as_ignore_background(true));
RSL.clear;
LAPDM.clear;
f_establish_mo(link_id);
RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
/* first transmission, P = 0 */
LAPDM.receive(t_PH_DATA(0, use_sacch, tr_LAPDm_I(link_id.sapi, c_r:=cr_MT_CMD, p:=false,
nr:=0, ns:=0, l3:=l3_mt)));
/* re-transmission, P = 1 */
LAPDM.receive(t_PH_DATA(0, use_sacch, tr_LAPDm_I(link_id.sapi, c_r:=cr_MT_CMD, p:=true,
nr:=0, ns:=0, l3:=l3_mt)));
deactivate(d);
/* We received one retrans, so peer is in LAPD_STATE_TIMER_RECOV state. Now send SABM: */
LAPDM.send(t_PH_DATA(link_id.sapi, use_sacch, ts_LAPDm_SABM(link_id.sapi, c_r:=cr_MO_CMD, p:=true, l3:=''O)));
T.start
alt {
[] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_UA(link_id.sapi, cr_MT_RSP, f:=true, l3:=''O))) {
received_ua := true;
if (not received_estind) {
repeat;
}
}
[] RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, link_id)) {
received_estind := true;
if (not received_ua) {
repeat;
}
}
[] RSL.receive { repeat; }
[] LAPDM.receive { repeat; }
[] T.timeout { setverdict(fail, "Timeout waiting for UA"); }
}
/* Test we can still send data afterwards */
l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
dls := valueof(t_init_LapdmDlState);
f_lapdm_transceive_mo(dls, link_id, l3_mo);
fp_common_fini();
}
testcase TC_normal_reestablishment_state_unacked() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_TC_normal_reestablishment_state_unacked));
}
control {
execute(TC_sabm_ua_dcch_sapi0());
execute(TC_sabm_ua_dcch_sapi0_nopayload());
execute(TC_sabm_ua_dcch_sapi3());
execute(TC_sabm_ua_dcch_sapi4());
execute(TC_sabm_contention());
execute(TC_sabm_retransmit());
execute(TC_sabm_retransmit_bts());
execute(TC_sabm_invalid_resp());
execute(TC_sabm_dm());
execute(TC_establish_ign_first_sabm());
execute(TC_iframe_seq_and_ack());
execute(TC_iframe_timer_recovery());
execute(TC_ns_seq_error());
execute(TC_nr_seq_error());
execute(TC_rec_invalid_frame());
execute(TC_segm_concat_dcch());
execute(TC_segm_concat_sacch());
execute(TC_t200_n200());
execute(TC_rr_response_frame_loss());
execute(TC_incorrect_cr());
execute(TC_sabm_incorrect_c());
execute(TC_normal_reestablishment());
execute(TC_normal_reestablishment_state_unacked());
}
}