blob: 07b785e889509094acc21b59e4411da8fd781946 [file] [log] [blame]
Harald Welte97cca2f2019-05-30 16:33:38 +02001module BTS_Tests_LAPDm {
Harald Weltef6543322017-07-16 07:35:10 +02002
Harald Welte72c81e72019-05-30 16:36:11 +02003import from GSM_Types all;
4import from Osmocom_Types all;
5import from LAPDm_RAW_PT all;
6import from LAPDm_Types all;
Harald Welte2f2b2b72019-05-31 22:24:57 +02007import from RSL_Types all;
Harald Welte72c81e72019-05-30 16:36:11 +02008import from BTS_Tests all;
Harald Welte2f2b2b72019-05-31 22:24:57 +02009import from Misc_Helpers all;
Harald Welte66110f02017-07-16 21:05:18 +020010
Harald Welte72c81e72019-05-30 16:36:11 +020011/* test that use exclusively only LAPDm over L1CTL */
12type component lapdm_test_CT {
13 port LAPDm_PT LAPDM;
14 var lapdm_CT lapdm_component;
15};
16
17/* contrary to BTS_Tests.ttcn, we use LAPDm_PT here, a convenience wrapper
18 * around L1CTL to perform encode/decode of abstract LAPDm frames */
Harald Welte72c81e72019-05-30 16:36:11 +020019
Harald Welte61332c02019-05-30 16:45:15 +020020/*********************************************************************************
21 * Test using only L1CTL/LAPDm
22 *********************************************************************************/
23
24function f_lapdm_init() runs on lapdm_test_CT {
Harald Welte72c81e72019-05-30 16:36:11 +020025 /* create the LAPDm component */
26 lapdm_component := lapdm_CT.create;
27 /* connect our own LAPDM port to the LAPDM Service Provider of the LAPDm component */
28 connect(self:LAPDM, lapdm_component:LAPDM_SP);
29 /* connect the LAPDm compoent's lower-side port to the system L1CTL port (which is internally
30 * connected to the Unix Domain Socket test port */
31 map(lapdm_component:L1CTL, system:L1CTL);
32
33 /* start the LAPDm parallel component calling it's local function LAPDmStart */
34 lapdm_component.start(LAPDmStart());
35}
36
Harald Welte2f2b2b72019-05-31 22:24:57 +020037function f_lapdm_exit() runs on lapdm_test_CT {
38 lapdm_component.stop;
39 lapdm_component.done;
40 unmap(lapdm_component:L1CTL, system:L1CTL);
41}
42
Harald Welte72c81e72019-05-30 16:36:11 +020043/* master function establishing a dedicated radio channel (takes care of RACH/IMM.ASS handling) */
44function f_establish_dcch() runs on lapdm_test_CT {
Harald Welte2f2b2b72019-05-31 22:24:57 +020045 var BCCH_tune_req tune_req := { arfcn := { false, mp_trx0_arfcn }, combined_ccch := true };
Harald Welte72c81e72019-05-30 16:36:11 +020046 var DCCH_establish_req est_req := { ra := 23 };
47
48 LAPDM.send(tune_req);
49 LAPDM.send(est_req);
50 LAPDM.receive(DCCH_establish_res:?);
51}
52
Harald Welte2f2b2b72019-05-31 22:24:57 +020053/* master function switching to a dedicated radio channel */
54function f_switch_dcch(Arfcn arfcn, RslChannelNr chan_nr, GsmTsc tsc) runs on lapdm_test_CT {
55 var BCCH_tune_req tune_req := { arfcn := arfcn, combined_ccch := true };
56 var DCCH_switch_req sw_req := { arfcn, chan_nr, tsc };
57
58 LAPDM.send(tune_req);
59 LAPDM.send(sw_req);
60 LAPDM.receive(DCCH_switch_res:?);
61}
62
Harald Welte72c81e72019-05-30 16:36:11 +020063/* helper function releasing dedicated radio channel physically (no Um signaling!) */
64function f_release_dcch() runs on lapdm_test_CT {
65 var DCCH_release_req rel_req := {};
66 LAPDM.send(rel_req);
67}
68
69template LAPDm_ph_data t_PH_DATA(template GsmSapi sapi, template boolean sacch, template LapdmFrame frame) := {
70 sacch := sacch,
71 sapi := sapi,
72 lapdm := frame
73}
74/* template for a valid SABM frame */
75template LapdmFrame LAPDm_B_SABM(template GsmSapi sapi, octetstring payload) := {
76 ab := {
77 addr := tr_LapdmAddr(sapi, false),
78 ctrl := tr_LapdmCtrlSABM(true),
79 len := lengthof(payload),
80 m := false,
81 el := 1,
82 payload := payload
Harald Weltec38611b2019-05-30 16:33:58 +020083 }
Harald Welte72c81e72019-05-30 16:36:11 +020084}
Harald Weltec38611b2019-05-30 16:33:58 +020085
Harald Welte72c81e72019-05-30 16:36:11 +020086/* template for a valid UA frame */
87template LapdmFrame tr_LAPDm_B_UA(template GsmSapi sapi, template octetstring payload) := {
88 ab := {
89 addr := tr_LapdmAddr(sapi, false),
90 ctrl := tr_LapdmCtrlUA(true),
91 len := ?,
92 m := false,
93 el := 1,
94 payload := payload
Harald Welte52c713c2017-07-16 15:44:44 +020095 }
Harald Welte72c81e72019-05-30 16:36:11 +020096}
Harald Welte52c713c2017-07-16 15:44:44 +020097
Harald Welte72c81e72019-05-30 16:36:11 +020098/* template for a valid UA frame */
99template LapdmFrame LAPDm_B_UA(template GsmSapi sapi, octetstring payload) := {
100 ab := {
101 addr := tr_LapdmAddr(sapi, false),
102 ctrl := tr_LapdmCtrlUA(true),
103 len := lengthof(payload),
104 m := false,
105 el := 1,
106 payload := payload
Harald Welte9e4725d2017-07-16 23:18:09 +0200107 }
Harald Welte72c81e72019-05-30 16:36:11 +0200108}
Harald Welte66110f02017-07-16 21:05:18 +0200109
Harald Welte72c81e72019-05-30 16:36:11 +0200110/* template for a valid UI frame */
111template LapdmFrame LAPDm_B_UI(template GsmSapi sapi, octetstring payload) := {
112 ab := {
113 addr := tr_LapdmAddr(sapi, true),
114 ctrl := tr_LapdmCtrlUI(false),
115 len := lengthof(payload),
116 m := false,
117 el := 1,
118 payload := payload
Harald Welte9e4725d2017-07-16 23:18:09 +0200119 }
Harald Welte72c81e72019-05-30 16:36:11 +0200120}
Harald Welte9e4725d2017-07-16 23:18:09 +0200121
Harald Welte72c81e72019-05-30 16:36:11 +0200122template LapdmFrame t_nopayload(template GsmSapi sapi) := {
123 ab := {
124 addr := tr_LapdmAddr(sapi, true),
125 ctrl := ?,
126 len := 0,
127 m := false,
128 el := 1,
129 payload := ''O
Harald Welted4ba7ff2017-07-17 21:00:48 +0200130 }
Harald Welte72c81e72019-05-30 16:36:11 +0200131}
132
133template LapdmFrame LAPDm_B_DISC(template GsmSapi sapi) modifies t_nopayload := {
134 ab := {
135 ctrl := tr_LapdmCtrlDISC(true)
136 }
137}
138
139template LapdmFrame LAPDm_B_RR(template GsmSapi sapi, template uint3_t nr) modifies t_nopayload := {
140 ab := {
141 ctrl := tr_LapdmCtrlRR(nr, false)
142 }
143}
144
145
146function f_test_sabm_results_in_ua(uint8_t sapi, boolean use_sacch, octetstring payload) runs on lapdm_test_CT return boolean {
147 var LAPDm_ph_data phd;
148 var boolean result := false;
149 timer T := 5.0;
150
151 f_establish_dcch();
152 LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, payload)));
153 log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload)));
154 T.start
155 alt {
156 [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { result := true; }
157 [] LAPDM.receive(t_PH_DATA(?, use_sacch, ?)) -> value phd { log("Other msg on DCH: ", phd); repeat; }
158 [] LAPDM.receive(t_PH_DATA(?, ?, ?)) -> value phd { log("Other PH-DATA: ", phd); repeat; }
159 [] T.timeout { }
160 }
161 LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_RR(sapi, 0)));
162 f_release_dcch();
163 return result;
164}
165
166testcase TC_sabm_ua_dcch_sapi0() runs on lapdm_test_CT {
Harald Welte61332c02019-05-30 16:45:15 +0200167 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200168 if (not f_test_sabm_results_in_ua(0, false, 'FEFE'O)) {
169 setverdict(fail);
170 }
171 setverdict(pass);
172}
173
174testcase TC_sabm_ua_dcch_sapi0_nopayload() runs on lapdm_test_CT {
Harald Welte61332c02019-05-30 16:45:15 +0200175 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200176 if (f_test_sabm_results_in_ua(0, false, ''O)) {
177 setverdict(fail, "Initial SABM/UA must contain L3 payload but BTS accepts without");
178 }
179 setverdict(pass);
180}
181
182testcase TC_sabm_ua_dcch_sapi3() runs on lapdm_test_CT {
Harald Welte61332c02019-05-30 16:45:15 +0200183 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200184 if (f_test_sabm_results_in_ua(3, false, 'FEFE'O)) {
185 setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=3");
186 }
187 setverdict(pass);
188}
189
190testcase TC_sabm_ua_dcch_sapi4() runs on lapdm_test_CT {
Harald Welte61332c02019-05-30 16:45:15 +0200191 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200192 if (f_test_sabm_results_in_ua(4, false, 'FEFE'O)) {
193 setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=4");
194 }
195 setverdict(pass);
196}
197
198testcase TC_sabm_contention() runs on lapdm_test_CT {
199 var LAPDm_ph_data phd;
200 const octetstring payload := '0102030405'O;
201 const GsmSapi sapi := 0;
202 const boolean use_sacch := false;
203 timer T := 5.0;
204
Harald Welte61332c02019-05-30 16:45:15 +0200205 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200206
207 f_establish_dcch();
208 /* first frame is our real SABM */
209 LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, payload)));
210 /* second frame is a SABM with different payload, which BTS has to ignore according to 8.4.1.4 */
211 LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, 'ABCDEF'O)));
212 log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload)));
213 T.start
214 alt {
215 [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { setverdict(pass); repeat; }
216 [] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_B_UA(sapi, ?))) {
217 setverdict(fail, "Second SABM was responded to during contention resolution");
Harald Welted4ba7ff2017-07-17 21:00:48 +0200218 }
Harald Welte72c81e72019-05-30 16:36:11 +0200219 [] LAPDM.receive { repeat };
220 [] T.timeout { }
Harald Welte9e4725d2017-07-16 23:18:09 +0200221 }
Harald Welte72c81e72019-05-30 16:36:11 +0200222 f_release_dcch();
223}
Harald Welte9e4725d2017-07-16 23:18:09 +0200224
Harald Welte72c81e72019-05-30 16:36:11 +0200225/* we test that a re-transmitted SABM with identical payload will result in the retransmission of a
226 * UA. This is required during the contention resolution procedure as specified in 8.4.1.4 */
227testcase TC_sabm_retransmit() runs on lapdm_test_CT {
228 const octetstring payload := '00FEFEDEADBEEF'O;
Harald Welte61332c02019-05-30 16:45:15 +0200229 f_lapdm_init();
Harald Welte72c81e72019-05-30 16:36:11 +0200230 if (not f_test_sabm_results_in_ua(0, false, payload)) {
231 setverdict(fail, "UA not received for first SABM");
Harald Welte599faa12017-07-17 21:49:24 +0200232 }
Harald Welte72c81e72019-05-30 16:36:11 +0200233 if (not f_test_sabm_results_in_ua(0, false, payload)) {
234 setverdict(fail, "UA not received for second SABM");
Harald Welted4ba7ff2017-07-17 21:00:48 +0200235 }
Harald Welte72c81e72019-05-30 16:36:11 +0200236 setverdict(pass);
237}
Harald Welted4ba7ff2017-07-17 21:00:48 +0200238
Harald Welte72c81e72019-05-30 16:36:11 +0200239testcase TC_foo() runs on lapdm_test_CT {
240 var LapdmFrame lf;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200241/*
Harald Welte72c81e72019-05-30 16:36:11 +0200242 var LapdmFrame lf := valueof(LAPDm_B_UA(0, ''O));
243 log("ENC UA: ", enc_LapdmFrame(lf));
244 lf := valueof(LAPDm_B_UI(0, ''O));
245 log("ENC UI B: ", enc_LapdmFrame(lf));
246 log("ENC UI B: ", enc_LapdmFrameB(lf.b));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200247
Harald Welte72c81e72019-05-30 16:36:11 +0200248 log("DEC UI AF: ", dec_LapdmAddressField('03'O));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200249*/
Harald Welteffcad682017-07-30 22:51:04 +0200250
Harald Welte72c81e72019-05-30 16:36:11 +0200251 lf := valueof(LAPDm_B_RR(0, 0));
252 log("ENC RR: ", enc_LapdmFrame(lf));
Harald Welteffcad682017-07-30 22:51:04 +0200253
Harald Welte72c81e72019-05-30 16:36:11 +0200254 lf := valueof(LAPDm_B_UA(0, ''O));
255 log("ENC UA: ", enc_LapdmFrame(lf));
Harald Welteffcad682017-07-30 22:51:04 +0200256
Harald Welte72c81e72019-05-30 16:36:11 +0200257 lf := valueof(LAPDm_B_UI(0, ''O));
258 log("ENC UI: ", enc_LapdmFrame(lf));
Harald Welteffcad682017-07-30 22:51:04 +0200259
Harald Welte72c81e72019-05-30 16:36:11 +0200260 log("DEC UI CU: ", dec_LapdmCtrlU('03'O));
261 log("DEC UI CT: ", dec_LapdmCtrl('03'O));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200262
Harald Welte72c81e72019-05-30 16:36:11 +0200263 log("DEC UA: ", dec_LapdmFrameAB('017301'O));
264 log("DEC UI: ", dec_LapdmFrameAB('030301'O));
265 log("DEC I: ", dec_LapdmFrameAB('030001'O));
266 log("DEC S: ", dec_LapdmFrameAB('030101'O));
267 log("DEC: ", dec_LapdmFrameAB('030301'O));
268 log("DEC: ", dec_LapdmFrameAB('0303012B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B'O));
269}
Harald Welted4ba7ff2017-07-17 21:00:48 +0200270
Harald Welte2f2b2b72019-05-31 22:24:57 +0200271/*********************************************************************************
272 * Test using both L1CTL/LAPDm and RSL
273 *********************************************************************************/
274
275private function fp_common_init() runs on ConnHdlr
276{
277 /* undo what f_start_handler is doing and pull LAPDm_CT into the loop */
278 unmap(self:L1CTL, system:L1CTL);
279 f_lapdm_init();
280 /* activate the channel on the BTS side */
281 f_rsl_chan_act(g_pars.chan_mode, false, {});
282 /* activate the channel on the MS side */
283 f_switch_dcch({false, mp_trx0_arfcn}, g_chan_nr, 7);
284}
285
286private function fp_common_fini() runs on ConnHdlr
287{
288 f_release_dcch();
289 f_rsl_chan_deact();
290 f_lapdm_exit();
291}
292
293/* Verify that the BTS is re-transmitting SABM messages after T200 timeout, inspired
294 by 3GPP TS 51.010-1 25.2.1.1.2.1 + 25.2.1.2.4 */
295private function f_TC_sabm_retransmit_bts(charstring id) runs on ConnHdlr {
296 const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
297 fp_common_init();
298
299 LAPDM.clear;
300 RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
301
302 timer T := 8.0;
303 var integer sabm_received := 0;
304 T.start;
305 alt {
306 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
307 sabm_received := sabm_received + 1;
308 repeat;
309 }
310 [] LAPDM.receive { repeat; }
311 [] T.timeout { }
312 }
313 if (sabm_received == 0) {
314 setverdict(fail, "No SABM observed at all!");
315 } else if (sabm_received != 6) {
316 setverdict(fail, "Incorrect number of SABM re-transmissions of observed: ",
317 sabm_received);
318 } else {
319 setverdict(pass, "Received ", sabm_received, " SABM");
320 }
321
322 fp_common_fini();
323}
324testcase TC_sabm_retransmit_bts() runs on test_CT {
325 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
326 f_testmatrix_each_chan(pars, refers(f_TC_sabm_retransmit_bts));
327}
328
329
330type record LapdmNamedFrame {
331 charstring name,
332 LapdmFrame lapdm
333};
334
335/* Test that the BTS will ignore receipt of frames other than a UA when
336 * received in response to the SABM frame, inspired from 3GPP TS 51.010-1
337 * Section 25.2.1.1.2.3 */
338private function f_TC_sabm_invalid_resp2(charstring id, LapdmNamedFrame err_frame) runs on ConnHdlr {
339 var integer sapi := err_frame.lapdm.ab.addr.sapi;
340 fp_common_init();
341
342 /* Establish Request via RSL; Expect SABM on LAPDm side */
343 LAPDM.clear;
344 RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
345 alt {
346 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O)));
347 [] LAPDM.receive { repeat; }
348 }
349
350 /* send erroneous response to SABM */
351 LAPDM.send(t_PH_DATA(0, false, err_frame.lapdm));
352
353 /* expect a SABM retransmission of the BTS */
354 timer T := 3.0;
355 T.start;
356 alt {
357 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
358 setverdict(pass);
359 }
360 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
361 [] LAPDM.receive(t_PH_DATA(0, false, ?)) {
362 setverdict(fail, "Received unexpected LAPDm frame instead of SABM after sending ",
363 err_frame.name);
364 }
365 [] LAPDM.receive { repeat; }
366 [] T.timeout {
367 setverdict(fail, "Timeout waiting for SABM retransmission after sending ",
368 err_frame.name);
369 }
370 }
371
372 fp_common_fini();
373}
374private function f_TC_sabm_invalid_resp(charstring id) runs on ConnHdlr {
375 const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
376 var LapdmNamedFrame err_frame[3] := {
377 { "I", valueof(ts_LAPDm_I(sapi, c_r := cr_MO_CMD, p := true, nr := 0, ns := 0,
378 l3 := '01020304'O)) },
379 { "RR", valueof(ts_LAPDm_RR(sapi, c_r := cr_MO_CMD, p := true, nr := 0)) },
380 { "REJ" , valueof(ts_LAPDm_REJ(sapi, c_r := cr_MO_CMD, p := true, nr := 0)) }
381 };
382 var integer i;
383
384 for (i := 0; i < lengthof(err_frame); i := i+1) {
385 f_TC_sabm_invalid_resp2(id, err_frame[i])
386 }
387}
388testcase TC_sabm_invalid_resp() runs on test_CT {
389 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
390 f_testmatrix_each_chan(pars, refers(f_TC_sabm_invalid_resp));
391}
392
393/* Test that the BTS will not re-transmit SABM frames after receiving a DM response,
394 * inspired from 3GPP TS 51.010-1 Section 25.2.1.1.3 */
395private function f_TC_sabm_dm(charstring id) runs on ConnHdlr {
396 const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
397 fp_common_init();
398
399 /* Establish Request via RSL; Expect SABM on LAPDm side */
400 LAPDM.clear;
401 RSL.send(ts_RSL_EST_REQ(g_chan_nr, ts_RslLinkID_DCCH(sapi)));
402 alt {
403 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O)));
404 [] LAPDM.receive { repeat; }
405 }
406
407 /* send DM response to SABM */
408 RSL.clear;
409 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_DM(sapi, c_r:=cr_MO_RSP, f:=true)));
410 alt {
411 [] RSL.receive(tr_RSL_REL_IND(g_chan_nr, tr_RslLinkID_DCCH(sapi)));
412 [] RSL.receive { repeat; }
413 }
414
415 /* expect no SABM retransmission of the BTS */
416 timer T := 3.0;
417 T.start;
418 alt {
419 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
420 setverdict(fail, "Received unexpected SABM retransmission");
421 }
422 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
423 [] LAPDM.receive(t_PH_DATA(0, false, ?)) {
424 setverdict(fail, "Received unexpected LAPDm frame");
425 }
426 [] LAPDM.receive { repeat; }
427 [] T.timeout {
428 setverdict(pass);
429 }
430 }
431
432 fp_common_fini();
433}
434testcase TC_sabm_dm() runs on test_CT {
435 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
436 f_testmatrix_each_chan(pars, refers(f_TC_sabm_dm));
437}
438
439/* Test the full LAPDm establishment while simulating the loss of the initial SABM or UA
440 * frame, requiring the BTS to re-transmit one SABM and then following up all the way to
441 * dedicated mode; inspired by 3GPP TS 51.010-1 25.2.1.2.2 */
442private function f_TC_establish_ign_first_sabm(charstring id) runs on ConnHdlr {
443 const integer sapi := 3; /* BTS may not establish SAPI=0 outbound */
444 var integer num_sabm := 0;
445 var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
446 timer T := 3.0;
447
448 fp_common_init();
449
450 /* Establish Request via RSL */
451 LAPDM.clear;
452 RSL.send(ts_RSL_EST_REQ(g_chan_nr, link_id));
453 /* Expect two SABM (retransmit) */
454 T.start;
455 alt {
456 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_SABM(sapi, ?, ?, ''O))) {
457 num_sabm := num_sabm + 1;
458 if (num_sabm < 2) {
459 repeat;
460 }
461 }
462 [] LAPDM.receive { repeat; }
463 }
464
465 /* send UA response to SABM */
466 RSL.clear;
467 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_UA(sapi, c_r:=cr_MO_RSP, f:=true, l3:=''O)));
468 alt {
469 [] RSL.receive(tr_RSL_EST_CONF(g_chan_nr, link_id));
470 [] RSL.receive { repeat; }
471 }
472
473 /* Send I frame from BTS to MS */
474 var octetstring l3 := f_rnd_octstring(10);
475 RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3));
476 alt {
477 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
478 nr:=0, ns:=0, l3:=l3)));
479 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) { repeat; }
480 [] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
481 [] LAPDM.receive { setverdict(fail, "Unexpected LAPDm received"); }
482 }
483 /* Send RR frame in response */
484 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=false, nr:=1)));
485
486 /* expect idle UI Frame from BTS */
487 alt {
488 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UI(0, ?, ''O))) {
489 setverdict(pass);
490 }
491 [] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
492 [] LAPDM.receive { setverdict(fail, "Unexpected LAPDm received"); }
493 }
494
495 fp_common_fini();
496}
497testcase TC_establish_ign_first_sabm() runs on test_CT {
498 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
499 f_testmatrix_each_chan(pars, refers(f_TC_establish_ign_first_sabm));
500}
501
502/* ignore all SACCH frames */
503private altstep as_lapdm_acch() runs on ConnHdlr {
504 [] LAPDM.receive(t_PH_DATA(0, true, ?)) { repeat; }
505}
Harald Welteef6fd442019-06-01 21:41:29 +0200506/* ignore all DCCH frames */
507private altstep as_lapdm_dcch() runs on ConnHdlr {
508 [] LAPDM.receive(t_PH_DATA(0, false, ?)) { repeat; }
509}
Harald Welte2f2b2b72019-05-31 22:24:57 +0200510/* ignore all LAPDm idle frames (UI) */
511private altstep as_lapdm_idle() runs on ConnHdlr {
Harald Welteef6fd442019-06-01 21:41:29 +0200512 [] LAPDM.receive(t_PH_DATA(0, ?, tr_LAPDm_UI(?, ?, ''O))) { repeat; }
Harald Welte2f2b2b72019-05-31 22:24:57 +0200513}
514/* ignore all measurement reports */
515private altstep as_rsl_meas_rep() runs on ConnHdlr {
516 [] RSL.receive(tr_RSL_MEAS_RES(g_chan_nr)) { repeat; }
517}
518/* fail if we receive an RSL ERROR IND */
519private altstep as_rsl_fail_err() runs on ConnHdlr {
520 var RSL_Message rx_rsl;
521 [] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, ?, ?)) {
522 setverdict(fail, "Received RSL ERROR IND ", rx_rsl);
523 }
524}
525/* all of the above */
Harald Welteef6fd442019-06-01 21:41:29 +0200526private altstep as_ignore_background(boolean want_dcch := true) runs on ConnHdlr {
527 [want_dcch] as_lapdm_acch();
528 [not want_dcch] as_lapdm_dcch();
Harald Welte2f2b2b72019-05-31 22:24:57 +0200529 [] as_lapdm_idle();
530 [] as_rsl_meas_rep();
531 [] as_rsl_fail_err();
532}
533
534/* Test the operation of Layer 2 sequence numbering.
535 * dedicated mode; inspired by 3GPP TS 51.010-1 25.2.2.1 */
536private function f_TC_iframe_seq_and_ack(charstring id) runs on ConnHdlr {
537 const integer sapi := 0;
538 var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
539 var octetstring l3 := f_rnd_octstring(18);
540 var default d;
541 timer T := 3.0;
542
543 fp_common_init();
544
545 /* some common altstep for meas res and other background noise */
546 d := activate(as_ignore_background());
547 RSL.clear;
548 LAPDM.clear;
549
550 /* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
551 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3)));
552 RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3));
553 /* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
554 LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3)));
555
556 var integer last_ns_rx := 0;
557
558 for (var integer i := 0; i < 10; i := i+1) {
559 var octetstring l3_mo := f_rnd_octstring(12);
560 var octetstring l3_mt := f_rnd_octstring(12);
561 var LAPDm_ph_data pd;
562
563 log("Starting iteration ", i);
564 /* MT I frame */
565 RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
566 alt {
567 /* SAPI = 0, R = 1, F = 0, M = 0, L = 0. */
568 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false,
569 nr:=i mod 8))) {
570 log("Ignoring RR in iteration ", i);
571 repeat;
572 }
573 /* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201. */
574 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
575 nr:=i mod 8, ns:=i mod 8,
576 l3:=l3_mt))) -> value pd {
577 last_ns_rx := pd.lapdm.ab.ctrl.i.n_s;
578 }
579 }
580 /* respond with MO I-frame: SAPI = 0, C = 0, P = 0, M = 0, 0 <= L <= N201. */
581 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
582 nr:=(last_ns_rx+1)mod 8,
583 ns:=i mod 8, l3 := l3_mo)));
584 RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
585 }
586 log("Completed iteration");
587
588 deactivate(d);
589 fp_common_fini();
590}
591testcase TC_iframe_seq_and_ack() runs on test_CT {
592 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
593 f_testmatrix_each_chan(pars, refers(f_TC_iframe_seq_and_ack));
594}
595
596/* To test that the BTS is able to respond to I frames whilst in the timer recovery state.
597 * Inspired by 3GPP TS 51.010-1 25.2.2.2 */
598
599/*
600 1) The BTS is brought into the multiple frame established state
601 2) The MS sends an L3 Request asking for IMEI to the MS.
602 3) The BTS shall respond with a RR frame though this may be incorporated with
603 the L3 Response I frame. The MS does not respond to the I frame.
604 4) The BTS shall wait for expiry of timer T200 and then repeat the I frame but
605 with the P bit set to 1.
606 5) The MS then sends a valid L3 Request I frame asking for IMEI which
607 does not acknowledge receipt of the I frame from the BTS.
608On the FACCH the BTS may send an RR frame acknowledging the I frame.
609 6) The BTS shall repeat the I frame, this frame will acknowledge receipt of
610 the second I frame from the MS.
611 7) The MS then acknowledges receipt of the MS I frame by sending a RR frame.
612 8) The BTS shall send the next I frame. The MS acknowledges this I frame.
613*/
614private function f_TC_iframe_timer_recovery(charstring id) runs on ConnHdlr {
615 const integer sapi := 0;
616 var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(sapi));
617 var default d;
618 timer T := 3.0;
619
620 fp_common_init();
621
622 /* some common altstep for meas res and other background noise */
623 d := activate(as_ignore_background());
624 RSL.clear;
625 LAPDM.clear;
626
627 var octetstring l3_mo := f_rnd_octstring(12);
628 var octetstring l3_mt := f_rnd_octstring(12);
629
630 /* 1) The BTS is brought into the multiple frame established state */
631
632 /* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
633 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
634 RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
635 /* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
636 LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
637
638 /* 2) The MS sends an L3 Request to the BTS */
639 l3_mo := f_rnd_octstring(18);
640 /* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201, N(S) = 0, N(R) = 0. * */
641 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
642 nr:=0, ns:=0, l3:=l3_mo)));
643 RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
644 /* 3) The BTS shall respond with a RR frame though this may be incorporated with
645 the L3 Response I frame. The MS does not respond to the I frame. */
646 RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
647 alt {
648 /* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 1. */
649 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=1))) {
650 repeat;
651 }
652 /* SAPI = 0, C = 0, P = 0, M = 0, 0 ≤ L ≤ N201, N(R) = 1, N(S) = 0 */
653 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
654 nr:=1, ns:=0, l3:=l3_mt)));
655 }
656
657 /* 4) The BTS shall wait for expiry of timer T200 and then repeat the I frame but
658 with the P bit set to 1. */
659 /* SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201, N(R) = 1, N(S) = 0. * */
660 LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=true,
661 nr:=1, ns:=0, l3:=l3_mt)));
662
663 /* 5) The MS then sends a valid L3 Request I frame asking for IMEI which
664 does not acknowledge receipt of the I frame from the BTS. */
665 /* SAPI = 0, C = 1, P = 0, M = 0, 0 ≤ L ≤ N201, N(S) = 1, N(R) = 0. * */
666 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_I(sapi, c_r:=cr_MO_CMD, p:=false,
667 nr:=0, ns:=1, l3 := l3_mo)));
668 RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3_mo));
669 alt {
670 /* On the FACCH the BTS may send an RR frame acknowledging the I frame. */
671 /* SAPI = 0, R = 1, F = 0, M = 0, L = 0, N(R) = 2. */
672 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_RR(sapi, c_r:=cr_MT_RSP, p:=false, nr:=2))) {
673 repeat;
674 }
675 /* 6) The BTS shall repeat the I frame, this frame will acknowledge
676 receipt of the second I frame from the MS. */
677 /* SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201, N(R) = 2, N(S) = 0. * */
678 [] LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=true,
679 nr:=2, ns:=0, l3:=l3_mt)));
680 }
681
682 /* 7) The MS then acknowledges receipt of the BTS I frame by sending a RR * frame. */
683 /* SAPI = 0, R = 0, F = 1, 0, M = 0, L = 0, N(R) = 1 */
684 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=true, nr:=1)));
685
686 /* 8) The BTS shall send the next I frame. The MS acknowledges this I frame. */
687 l3_mt := f_rnd_octstring(16);
688 RSL.send(ts_RSL_DATA_REQ(g_chan_nr, link_id, l3_mt));
689 /* SAPI = 0, C = 0, P = 0, M = 0, 0 ≤ L ≤ N201, N(R) = 2, N(S) = 1 */
690 LAPDM.receive(t_PH_DATA(0, false, tr_LAPDm_I(sapi, c_r:=cr_MT_CMD, p:=false,
691 nr:=2, ns:=1, l3:=l3_mt)));
692 LAPDM.send(t_PH_DATA(0, false, ts_LAPDm_RR(sapi, c_r:=cr_MO_RSP, p:=true, nr:=2)));
693
694 deactivate(d);
695 fp_common_fini();
696}
697testcase TC_iframe_timer_recovery() runs on test_CT {
698 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
699 f_testmatrix_each_chan(pars, refers(f_TC_iframe_timer_recovery));
700}
701
Harald Welte9ea918c2019-06-01 11:46:25 +0200702type record LapdmDlConfig {
703 integer n201,
704 integer t200
705};
706
707type record LapdmDlState {
708 integer v_s,
709 integer v_a,
710 integer v_r
711};
712
713template (value) LapdmDlState t_init_LapdmDlState := {
714 v_s := 0,
715 v_a := 0,
716 v_r := 0
717}
718
719private function inc_mod8(inout integer v)
720{
721 v := (v + 1) mod 8;
722}
723
724private function f_lapdm_transceive_mo(inout LapdmDlState dls, RslLinkId link_id, octetstring l3)
725runs on ConnHdlr {
726 var LAPDm_ph_data pd;
727 var integer offset := 0;
728 var integer n201 := 20;
729 var boolean is_sacch := false;
730 if (link_id.c == SACCH) {
731 n201 := 18;
732 is_sacch := true;
733 }
734
735 while (offset < lengthof(l3)) {
736 var integer remain_len := lengthof(l3) - offset;
737 var integer seg_len := remain_len;
738 if (remain_len > n201) {
739 seg_len := n201;
740 }
741 var octetstring segment := substr(l3, offset, seg_len);
742 var boolean more;
743 if (offset + lengthof(segment) < lengthof(l3)) {
744 more := true;
745 } else {
746 more := false;
747 }
748 /* send the next segment */
749 LAPDM.send(t_PH_DATA(0, is_sacch,
750 ts_LAPDm_I(link_id.sapi, c_r:=cr_MO_CMD, p:=false,
751 nr:=dls.v_a, ns:=dls.v_s, l3:=segment, m:=more)));
752 inc_mod8(dls.v_s);
753 offset := offset + lengthof(segment);
754
755 /* wait for it to be acknowledged */
756 alt {
757 [] LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_RR(link_id.sapi, c_r:=cr_MT_RSP,
758 p:=false, nr:=(dls.v_s) mod 8)));
Harald Welteef6fd442019-06-01 21:41:29 +0200759 [] as_ignore_background(not is_sacch);
Harald Welte9ea918c2019-06-01 11:46:25 +0200760 [] LAPDM.receive(t_PH_DATA(0, is_sacch, ?)) -> value pd {
761 setverdict(fail, "received unexpected LAPDm ", pd);
762 repeat;
763 }
764 [] LAPDM.receive(t_PH_DATA(0, ?, ?)) { repeat; }
765 [offset < lengthof(l3)] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, ?)) {
766 setverdict(fail, "received RSL DATA IND before message complete");
767 }
768 }
769 }
770
771 timer T := 1.0;
772 T.start;
773 alt {
774 [] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3)) {
775 setverdict(pass);
776 }
777 [] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, ?)) {
778 setverdict(fail, "Received RSL DATA IND with wrong payload");
779 }
780 [] T.timeout {
781 setverdict(fail, "Timeout waiting for RSL DATA IND of de-segmented message");
782 }
783 }
784}
785
786/* Section 5.8.5 of TS 04.06 */
787const integer c_TS0406_MAX_L3_OCTETS := 251;
788
Harald Welteef6fd442019-06-01 21:41:29 +0200789/* test segmentation and de-segmentation (concatenation) of a large message in uplink
790 * on specified SAPI/channel */
791private function f_TC_segm_concat(charstring id, RslLinkId link_id) runs on ConnHdlr {
792 var integer sapi := link_id.sapi;
793 var boolean is_sacch := false;
794 if (link_id.c == SACCH) {
795 is_sacch := true;
796 }
Harald Welte9ea918c2019-06-01 11:46:25 +0200797 var default d;
798 timer T := 3.0;
799
800 fp_common_init();
801
802 /* some common altstep for meas res and other background noise */
Harald Welteef6fd442019-06-01 21:41:29 +0200803 d := activate(as_ignore_background(not is_sacch));
Harald Welte9ea918c2019-06-01 11:46:25 +0200804 RSL.clear;
805 LAPDM.clear;
806
807 var octetstring l3_mo := f_rnd_octstring(5);
808
Harald Welte9ea918c2019-06-01 11:46:25 +0200809 /* MO Establish Request via LADPm: SAPI = 0, C = 0, P = 1, M = 0, 0 ≤ L ≤ N201.. */
Harald Welteef6fd442019-06-01 21:41:29 +0200810 if (is_sacch) {
811 /* no payload permitted, as this is not contention resolution */
812 l3_mo := ''O;
813 LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
814 RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, link_id));
815 } else {
816 LAPDM.send(t_PH_DATA(0, is_sacch, ts_LAPDm_SABM(sapi, c_r:=cr_MO_CMD, p:=true, l3:=l3_mo)));
817 RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3_mo));
818 }
Harald Welte9ea918c2019-06-01 11:46:25 +0200819 /* UA: SAPI = 0, R = 0, F = 1, M = 0, L = L of SABM. */
Harald Welteef6fd442019-06-01 21:41:29 +0200820 LAPDM.receive(t_PH_DATA(0, is_sacch, tr_LAPDm_UA(sapi, cr_MT_RSP, f:=true, l3:=l3_mo)));
Harald Welte9ea918c2019-06-01 11:46:25 +0200821
822 l3_mo := f_rnd_octstring(c_TS0406_MAX_L3_OCTETS);
823
824 deactivate(d);
825
826 var LapdmDlState dls := valueof(t_init_LapdmDlState);
827 f_lapdm_transceive_mo(dls, link_id, l3_mo);
828
829 fp_common_fini();
830}
Harald Welteef6fd442019-06-01 21:41:29 +0200831private function f_TC_segm_concat_dcch(charstring id) runs on ConnHdlr {
832 f_TC_segm_concat(id, valueof(ts_RslLinkID_DCCH(0)));
Harald Welte9ea918c2019-06-01 11:46:25 +0200833}
Harald Welteef6fd442019-06-01 21:41:29 +0200834private function f_TC_segm_concat_sacch(charstring id) runs on ConnHdlr {
835 f_TC_segm_concat(id, link_id :=valueof(ts_RslLinkID_SACCH(0)));
836}
837/* test mobile-originated segmentation/de-segmentation on DCCH */
838testcase TC_segm_concat_dcch() runs on test_CT {
839 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
840 f_testmatrix_each_chan(pars, refers(f_TC_segm_concat_dcch));
841}
842/* test mobile-originated segmentation/de-segmentation on SACCH */
843testcase TC_segm_concat_sacch() runs on test_CT {
844 var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
845 f_testmatrix_each_chan(pars, refers(f_TC_segm_concat_sacch));
846}
847
Harald Welte2f2b2b72019-05-31 22:24:57 +0200848
849
Harald Welte72c81e72019-05-30 16:36:11 +0200850control {
851 execute(TC_foo());
852 execute(TC_sabm_ua_dcch_sapi0());
853 execute(TC_sabm_ua_dcch_sapi0_nopayload());
854 execute(TC_sabm_ua_dcch_sapi3());
855 execute(TC_sabm_ua_dcch_sapi4());
856 execute(TC_sabm_contention());
857 execute(TC_sabm_retransmit());
Harald Welte2f2b2b72019-05-31 22:24:57 +0200858 execute(TC_sabm_retransmit_bts());
859 execute(TC_sabm_invalid_resp());
860 execute(TC_sabm_dm());
861 execute(TC_establish_ign_first_sabm());
862 execute(TC_iframe_seq_and_ack());
863 execute(TC_iframe_timer_recovery());
Harald Welteef6fd442019-06-01 21:41:29 +0200864 execute(TC_segm_concat_dcch());
865 execute(TC_segm_concat_sacch());
Harald Welte72c81e72019-05-30 16:36:11 +0200866}
867
Harald Weltef6543322017-07-16 07:35:10 +0200868}