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 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 establishing a dedicated radio channel (takes care of RACH/IMM.ASS handling) */ function f_establish_dcch() runs on lapdm_test_CT { var BCCH_tune_req tune_req := { arfcn := { false, mp_trx0_arfcn }, combined_ccch := true }; var DCCH_establish_req est_req := { ra := 23 }; LAPDM.send(tune_req); LAPDM.send(est_req); LAPDM.receive(DCCH_establish_res:?); } /* master function switching to a dedicated radio channel */ function f_switch_dcch(Arfcn arfcn, RslChannelNr chan_nr, GsmTsc tsc) runs on lapdm_test_CT { var BCCH_tune_req tune_req := { arfcn := arfcn, combined_ccch := true }; var DCCH_switch_req sw_req := { arfcn, chan_nr, tsc }; 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 } /* template for a valid SABM frame */ template LapdmFrame LAPDm_B_SABM(template GsmSapi sapi, octetstring payload) := { ab := { addr := tr_LapdmAddr(sapi, false), ctrl := tr_LapdmCtrlSABM(true), len := lengthof(payload), m := false, el := 1, payload := payload } } /* template for a valid UA frame */ template LapdmFrame tr_LAPDm_B_UA(template GsmSapi sapi, template octetstring payload) := { ab := { addr := tr_LapdmAddr(sapi, false), ctrl := tr_LapdmCtrlUA(true), len := ?, m := false, el := 1, payload := payload } } /* template for a valid UA frame */ template LapdmFrame LAPDm_B_UA(template GsmSapi sapi, octetstring payload) := { ab := { addr := tr_LapdmAddr(sapi, false), ctrl := tr_LapdmCtrlUA(true), len := lengthof(payload), m := false, el := 1, payload := payload } } /* template for a valid UI frame */ template LapdmFrame LAPDm_B_UI(template GsmSapi sapi, octetstring payload) := { ab := { addr := tr_LapdmAddr(sapi, true), ctrl := tr_LapdmCtrlUI(false), len := lengthof(payload), m := false, el := 1, payload := payload } } template LapdmFrame t_nopayload(template GsmSapi sapi) := { ab := { addr := tr_LapdmAddr(sapi, true), ctrl := ?, len := 0, m := false, el := 1, payload := ''O } } template LapdmFrame LAPDm_B_DISC(template GsmSapi sapi) modifies t_nopayload := { ab := { ctrl := tr_LapdmCtrlDISC(true) } } template LapdmFrame LAPDm_B_RR(template GsmSapi sapi, template uint3_t nr) modifies t_nopayload := { ab := { ctrl := tr_LapdmCtrlRR(nr, false) } } function f_test_sabm_results_in_ua(uint8_t sapi, boolean use_sacch, octetstring payload) runs on lapdm_test_CT return boolean { var LAPDm_ph_data phd; var boolean result := false; timer T := 5.0; f_establish_dcch(); LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, payload))); log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload))); T.start alt { [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { result := true; } [] LAPDM.receive(t_PH_DATA(?, use_sacch, ?)) -> value phd { log("Other msg on DCH: ", phd); repeat; } [] LAPDM.receive(t_PH_DATA(?, ?, ?)) -> value phd { log("Other PH-DATA: ", phd); repeat; } [] T.timeout { } } LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_RR(sapi, 0))); f_release_dcch(); return result; } testcase TC_sabm_ua_dcch_sapi0() runs on lapdm_test_CT { f_lapdm_init(); if (not f_test_sabm_results_in_ua(0, false, 'FEFE'O)) { setverdict(fail); } setverdict(pass); } testcase TC_sabm_ua_dcch_sapi0_nopayload() runs on lapdm_test_CT { f_lapdm_init(); 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); } testcase TC_sabm_ua_dcch_sapi3() runs on lapdm_test_CT { f_lapdm_init(); 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); } testcase TC_sabm_ua_dcch_sapi4() runs on lapdm_test_CT { f_lapdm_init(); 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); } testcase TC_sabm_contention() runs on lapdm_test_CT { var LAPDm_ph_data phd; const octetstring payload := '0102030405'O; const GsmSapi sapi := 0; const boolean use_sacch := false; timer T := 5.0; f_lapdm_init(); f_establish_dcch(); /* first frame is our real SABM */ LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, 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, LAPDm_B_SABM(sapi, 'ABCDEF'O))); log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload))); T.start alt { [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { setverdict(pass); repeat; } [] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_B_UA(sapi, ?))) { setverdict(fail, "Second SABM was responded to during contention resolution"); } [] LAPDM.receive { repeat }; [] T.timeout { } } f_release_dcch(); } /* 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 */ testcase TC_sabm_retransmit() runs on lapdm_test_CT { const octetstring payload := '00FEFEDEADBEEF'O; f_lapdm_init(); if (not f_test_sabm_results_in_ua(0, false, payload)) { setverdict(fail, "UA not received for first SABM"); } if (not f_test_sabm_results_in_ua(0, false, payload)) { setverdict(fail, "UA not received for second SABM"); } setverdict(pass); } testcase TC_foo() runs on lapdm_test_CT { var LapdmFrame lf; /* var LapdmFrame lf := valueof(LAPDm_B_UA(0, ''O)); log("ENC UA: ", enc_LapdmFrame(lf)); lf := valueof(LAPDm_B_UI(0, ''O)); log("ENC UI B: ", enc_LapdmFrame(lf)); log("ENC UI B: ", enc_LapdmFrameB(lf.b)); log("DEC UI AF: ", dec_LapdmAddressField('03'O)); */ lf := valueof(LAPDm_B_RR(0, 0)); log("ENC RR: ", enc_LapdmFrame(lf)); lf := valueof(LAPDm_B_UA(0, ''O)); log("ENC UA: ", enc_LapdmFrame(lf)); lf := valueof(LAPDm_B_UI(0, ''O)); log("ENC UI: ", enc_LapdmFrame(lf)); log("DEC UI CU: ", dec_LapdmCtrlU('03'O)); log("DEC UI CT: ", dec_LapdmCtrl('03'O)); log("DEC UA: ", dec_LapdmFrameAB('017301'O)); log("DEC UI: ", dec_LapdmFrameAB('030301'O)); log("DEC I: ", dec_LapdmFrameAB('030001'O)); log("DEC S: ", dec_LapdmFrameAB('030101'O)); log("DEC: ", dec_LapdmFrameAB('030301'O)); log("DEC: ", dec_LapdmFrameAB('0303012B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B'O)); } /********************************************************************************* * 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(); /* 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({false, mp_trx0_arfcn}, g_chan_nr, 7); } 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) runs on ConnHdlr { [want_dcch] as_lapdm_acch(); [not want_dcch] as_lapdm_dcch(); [] as_lapdm_idle(); [] as_rsl_meas_rep(); [] as_rsl_fail_err(); } /* 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)); } 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 { setverdict(fail, "received unexpected LAPDm ", pd); repeat; } [] LAPDM.receive(t_PH_DATA(0, ?, ?)) { repeat; } [offset < lengthof(l3)] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, ?)) { setverdict(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, ?)) { setverdict(fail, "Received RSL DATA IND with wrong payload"); } [] T.timeout { setverdict(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)); } control { execute(TC_foo()); 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_segm_concat_dcch()); execute(TC_segm_concat_sacch()); execute(TC_t200_n200()); execute(TC_rr_response_frame_loss()); execute(TC_incorrect_cr()); } }