From cc0b0149038eb4159bad511d171eedd3cf4b9a96 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 29 May 2018 15:19:33 +0200 Subject: bsc: Add LCLS related test cases This is an early WIP, we actually will need to establish two calls/legs before the BSC is able to locally correlate them. Related: OS#1602 Change-Id: Ie6d0b9c38027abf65c7c564fc79b889d013fa6a7 --- bsc/BSC_Tests.ttcn | 2 - bsc/BSC_Tests_LCLS.ttcn | 579 +++++++++++++++++++++++++++++++++++++++++ bsc/MSC_ConnectionHandler.ttcn | 45 +++- library/BSSMAP_Templates.ttcn | 12 + 4 files changed, 634 insertions(+), 4 deletions(-) create mode 100644 bsc/BSC_Tests_LCLS.ttcn diff --git a/bsc/BSC_Tests.ttcn b/bsc/BSC_Tests.ttcn index 80fb8c36..3830e7d4 100644 --- a/bsc/BSC_Tests.ttcn +++ b/bsc/BSC_Tests.ttcn @@ -1830,8 +1830,6 @@ testcase TC_assignment_codec_amr_h() runs on test_CT { vc_conn.done; } - - /* test if L3 RR CLASSMARK CHANGE is translated to BSSMAP CLASSMARK UPDATE */ private function f_tc_classmark(charstring id) runs on MSC_ConnHdlr { g_pars := valueof(t_def_TestHdlrPars); diff --git a/bsc/BSC_Tests_LCLS.ttcn b/bsc/BSC_Tests_LCLS.ttcn new file mode 100644 index 00000000..42feb573 --- /dev/null +++ b/bsc/BSC_Tests_LCLS.ttcn @@ -0,0 +1,579 @@ +module BSC_Tests_LCLS { + +/* Integration Tests for OsmoBSC + * (C) 2018 by Harald Welte + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * This test suite tests OsmoBSC while emulating both multiple BTS + MS as + * well as the MSC. See README for more details. + * + * There are test cases that run in so-called 'handler mode' and test cases + * that run directly on top of the BSSAP and RSL CodecPorts. The "handler mode" + * tests abstract the multiplexing/demultiplexing of multiple SCCP connections + * and/or RSL channels and are hence suitable for higher-level test cases, while + * the "raw" tests directly on top of the CodecPorts are more suitable for lower- + * level testing. + */ + +import from General_Types all; +import from Osmocom_Types all; +import from GSM_Types all; +import from IPL4asp_Types all; + +import from BSSAP_Types all; +import from BSSAP_Adapter all; +import from BSSAP_CodecPort all; +import from BSSMAP_Templates all; +import from IPA_Emulation all; +import from IPA_CodecPort all; +import from IPA_Types all; +import from RSL_Types all; +import from RSL_Emulation all; +import from MGCP_Types all; +import from MGCP_Emulation all; +import from MGCP_Templates all; +import from SDP_Types all; + +import from Osmocom_CTRL_Functions all; +import from Osmocom_CTRL_Types all; +import from Osmocom_CTRL_Adapter all; + +import from Osmocom_VTY_Functions all; +import from TELNETasp_PortType all; + +import from MobileL3_CommonIE_Types all; +import from MobileL3_Types all; +import from L3_Templates all; +import from GSM_RR_Types all; + +import from BSSMAP_Templates all; +import from BSSMAP_Emulation all; + +import from MSC_ConnectionHandler all; +import from BSC_Tests all; + +/* The philosophy of this testsuite is to re-use as much as possible the existing components + * and functions that we have in BSC_Tests and its dependencies. However, as opposed to those + * normal BSC tests, we here have to run *two* ConnHdlr and synchronize activity between them. + * + * We do this by adding some special-purpose ports between the main test component running the + * test case [lcls_]test_CT and the per-connection [LCLS_]MSC_ConnHdlr. + */ + + +/* take test_CT from BSC_Tests and extend it with LCLS specific bits */ +type component lcls_test_CT extends test_CT { + /* Component references */ + var LCLS_MSC_ConnHdlr vc_CONN_A; + var LCLS_MSC_ConnHdlr vc_CONN_B; + /* Ports to the two call legs */ + port LCLS_InterComp_PT CONN_A; + port LCLS_InterComp_PT CONN_B; +} + +/* take MSC_ConnHdlr and extend it with LCLS specific bits */ +type component LCLS_MSC_ConnHdlr extends MSC_ConnHdlr { + /* Port back to the controlling lcls_test_CT */ + port LCLS_InterComp_PT MASTER; +} + +/* port type between lcls_test_CT and LCLS_MSC_ConnHdlr */ +type port LCLS_InterComp_PT message { + /* BSSAP from BSSA_ConnHdlr */ + inout PDU_BSSAP, BSSAP_Conn_Prim, PDU_DTAP_MO, PDU_DTAP_MT, + /* RSL from RSL_DchanHdlr */ + RSLDC_ChanRqd, RSL_Message, + /* MGCP from MGCP_ConnHdlr */ + MgcpCommand, MgcpResponse, + LclsCompSync; +} with { extension "internal" }; + +type enumerated LclsCompSync { + /* ConnHdlr signals to master component that assignment has completed */ + LCLS_COMP_SYNC_ASS_COMPL +} + + +/* forward messages between the RSL/MGCP/BSSAP Emulation and the master component */ +private altstep as_lcls_conn_hdlr_proxy() runs on LCLS_MSC_ConnHdlr { + var PDU_BSSAP bssap; + var BSSAP_Conn_Prim bssap_p; + var PDU_DTAP_MO dtap_mo; + var PDU_DTAP_MT dtap_mt; + var MgcpCommand mgcp_cmd; + var MgcpResponse mgcp_rsp; + var RSL_Message rsl_msg; + /* from ConnHdlr to master process */ + [] BSSAP.receive(PDU_BSSAP:?) -> value bssap { MASTER.send(bssap); } + [] BSSAP.receive(BSSAP_Conn_Prim:?) -> value bssap_p { MASTER.send(bssap_p); } + [] BSSAP.receive(PDU_DTAP_MO:?) -> value dtap_mo { MASTER.send(dtap_mo); } + [] BSSAP.receive(PDU_DTAP_MT:?) -> value dtap_mt { MASTER.send(dtap_mt); } + [] MGCP.receive(MgcpCommand:?) -> value mgcp_cmd { MASTER.send(mgcp_cmd); } + [] MGCP.receive(MgcpResponse:?) -> value mgcp_rsp { MASTER.send(mgcp_rsp); } + [] RSL.receive(RSL_Message:?) -> value rsl_msg { MASTER.send(rsl_msg); } + /* from master process to ConnHdlr */ + [] MASTER.receive(PDU_BSSAP:?) -> value bssap { BSSAP.send(bssap); } + [] MASTER.receive(PDU_DTAP_MO:?) -> value dtap_mo { BSSAP.send(dtap_mo); } + [] MASTER.receive(PDU_DTAP_MT:?) -> value dtap_mt { BSSAP.send(dtap_mt); } + [] MASTER.receive(MgcpCommand:?) -> value mgcp_cmd { MGCP.send(mgcp_cmd); } + [] MASTER.receive(MgcpResponse:?) -> value mgcp_rsp { MGCP.send(mgcp_rsp); } + [] MASTER.receive(RSL_Message:?) -> value rsl_msg { RSL.send(rsl_msg); } +} + + +private function f_lcls_connhdlr_main(charstring id) runs on LCLS_MSC_ConnHdlr { + /* 1) establish the connection between RSL and BSSAP side */ + var PDU_BSSAP ass_req := f_gen_ass_req(); + var template PDU_BSSAP ass_compl := f_gen_exp_compl(); + ass_req.pdu.bssmap.assignmentRequest.codecList := g_pars.ass_codec_list; + f_establish_fully(ass_req, ass_compl); + + /* 2) notify master that assignment has completed */ + MASTER.send(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + + /* 3) proxy packets between master component and various ports */ + while (true) { + as_lcls_conn_hdlr_proxy(); + } +} + +type function lcls_void_fn(charstring id) runs on LCLS_MSC_ConnHdlr; + +/* first function inside ConnHdlr component; sets g_pars + starts function */ +private function f_handler_init(lcls_void_fn fn, charstring id, template (omit) TestHdlrParams pars := omit) +runs on LCLS_MSC_ConnHdlr { + if (isvalue(pars)) { + g_pars := valueof(pars); + } + fn.apply(id); +} + +/* function creating the two ConnHdlrs, connecting them + starting them */ +private function f_lcls_test_init(TestHdlrParams pars_a, TestHdlrParams pars_b) runs on lcls_test_CT { + var charstring id_a := testcasename() & "-A"; + var charstring id_b := testcasename() & "-B"; + + pars_b.imsi := '002029876543210'H; + pars_b.media_nr := 2; + + /* create and connect the two ConnHandlers */ + vc_CONN_A := LCLS_MSC_ConnHdlr.create(id_a); + f_connect_handler(vc_CONN_A); + connect(vc_CONN_A:MASTER, self:CONN_A); + + vc_CONN_B := LCLS_MSC_ConnHdlr.create(id_b); + f_connect_handler(vc_CONN_B); + connect(vc_CONN_B:MASTER, self:CONN_B); + + /* start the two components */ + vc_CONN_A.start(f_handler_init(refers(f_lcls_connhdlr_main), id_a, pars_a)); + f_sleep(3.0); + vc_CONN_B.start(f_handler_init(refers(f_lcls_connhdlr_main), id_b, pars_b)); +} + +private function f_lcls_test_fini() runs on lcls_test_CT { + vc_CONN_A.stop; + vc_CONN_B.stop; +} + +/* ignore some messages which we're not interested in evaluating (yet) */ +private altstep as_ignore() runs on lcls_test_CT { + [] CONN_A.receive(tr_DLCX) { repeat; } + [] CONN_B.receive(tr_DLCX) { repeat; } +} + +/* fail if any notify is being received */ +private altstep as_fail_on_lcls_notify() runs on lcls_test_CT +{ + [] CONN_A.receive(tr_BSSMAP_LclsNotification(?, *)) { + setverdict(fail, "Unexpected BSSMAP LCLS Notification"); + } + [] CONN_B.receive(tr_BSSMAP_LclsNotification(?, *)) { + setverdict(fail, "Unexpected BSSMAP LCLS Notification"); + } +} + +private function f_wait_fail_notify() runs on lcls_test_CT +{ + timer T := 3.0; + T.start; + alt { + [] as_fail_on_lcls_notify(); + [] T.timeout { } + } +} + +private function f_lcls_init(integer nr_bts := 1) runs on lcls_test_CT +{ + var default d; + + d := activate(as_ignore()); + f_init(nr_bts, true); + f_sleep(1.0); +} + + +/* Send an ASSIGNMENT REQ with LCLS GCR only, without LCLS CFG or CSC */ +testcase TC_lcls_gcr_only() runs on lcls_test_CT { + var TestHdlrParams pars := valueof(t_def_TestHdlrPars); + var MSC_ConnHdlr vc_conn; + + f_lcls_init(); + + pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + /* Expect LCLS status to be not reported, as no LCLS config was signalled */ + pars.lcls.exp_sts := omit; + + f_lcls_test_init(pars, pars); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + f_wait_fail_notify(); + f_lcls_test_fini(); +} + +/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect connect both-way */ +testcase TC_lcls_gcr_bway_connect() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + var MgcpCommand mgcp_cmd; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_connect; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* second call should then reuslt in LS */ + pars_b.lcls.exp_sts := LCLS_STS_locally_switched; + + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + interleave { + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched)); + [] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + [] CONN_A.receive(tr_MDCX) -> value mgcp_cmd { + CONN_A.send(f_build_mdcx_rsp(mgcp_cmd)); + } + } + + f_lcls_test_fini(); +} + +/* Send an ASSIGNMENT REQ with LCLS CFG+CSC enabling LCLS but GCR doesn't match! */ +testcase TC_lcls_gcr_nomatch_bway_connect() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + var MgcpCommand mgcp_cmd; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_connect; + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_b.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090b'O)); + + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + f_wait_fail_notify(); + f_lcls_test_fini(); +} + + +/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */ +testcase TC_lcls_gcr_bway_dont_connect() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_do_not_connect; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* Expect LCLS is *NOT* established */ + pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls; + + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + f_wait_fail_notify(); + f_lcls_test_fini(); +} + +/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */ +testcase TC_lcls_gcr_unsuppported_cfg() runs on lcls_test_CT { + var TestHdlrParams pars := valueof(t_def_TestHdlrPars); + var MSC_ConnHdlr vc_conn; + + f_lcls_init(); + + pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars.lcls.cfg := LCLS_CFG_both_way_and_send_DL; + pars.lcls.csc := LCLS_CSC_connect; + /* Expect LCLS is *NOT* established with "LCLS_STS_req_lcls_not_supp" */ + pars.lcls.exp_sts := LCLS_STS_req_lcls_not_supp; + + f_lcls_test_init(pars, pars); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + f_wait_fail_notify(); + f_lcls_test_fini(); +} + +/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */ +testcase TC_lcls_gcr_unsuppported_csc() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_bicast_UL_and_recv_DL_at_handover; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* Expect LCLS is *NOT* established */ + pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls; + + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + f_lcls_test_fini(); +} + +/* Send an ASSIGNMENT REQ with "do not connect" and enable later using LCLS CTRL */ +testcase TC_lcls_gcr_bway_dont_connect_csc() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + var MgcpCommand mgcp_cmd; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_do_not_connect; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* Expect LCLS is *NOT* established */ + pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls; + + /* start call and expect it to be "not yet" LS */ + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + f_sleep(2.0); + + /* send "connect" on A side, expect call to remain in "not yet" */ + CONN_A.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_connect))); + CONN_A.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_not_yet_ls))); + f_sleep(2.0); + + /* send "connect" on B side, expect call to go LS, with notify to A side */ + CONN_B.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_connect))); + interleave { + [] CONN_B.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_locally_switched))); + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched)); + [] CONN_A.receive(tr_MDCX) -> value mgcp_cmd { + CONN_A.send(f_build_mdcx_rsp(mgcp_cmd)); + } + [] CONN_B.receive(tr_MDCX) -> value mgcp_cmd { + CONN_B.send(f_build_mdcx_rsp(mgcp_cmd)); + } + } + f_wait_fail_notify(); + f_lcls_test_fini(); +} + +private function f_build_mdcx_rsp(MgcpCommand mdcx) return MgcpResponse +{ + var MgcpConnectionId conn_id := f_MgcpCmd_extract_conn_id(mdcx); + var SDP_Message sdp_in := mdcx.sdp; + var MgcpResponse resp; + var SDP_Message sdp_out; + var integer rtp_pt := str2int(sdp_in.media_list[0].media_field.fmts[0]); + + sdp_out := valueof(ts_SDP(sdp_in.connection.conn_addr.addr, sdp_in.connection.conn_addr.addr, + "foo", "21", sdp_in.media_list[0].media_field.ports.port_number, + { int2str(rtp_pt) }, + { valueof(ts_SDP_rtpmap(rtp_pt, "AMR/8000")), + valueof(ts_SDP_ptime(20)) } )); + return valueof(ts_MDCX_ACK(mdcx.line.trans_id, conn_id, sdp_out)); +} + +/* Establish LCLS "connect" followed by a MSC-initiated break */ +testcase TC_lcls_connect_break() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + var MgcpCommand mgcp_cmd; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_connect; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* second call should then reuslt in LS */ + pars_b.lcls.exp_sts := LCLS_STS_locally_switched; + + /* Expect LS to be established successfully */ + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + interleave { + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched)); + [] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + [] CONN_A.receive(tr_MDCX) -> value mgcp_cmd { + CONN_A.send(f_build_mdcx_rsp(mgcp_cmd)); + } +/* not needed, as this MDCX is still handled within MSC_ConnectionHandler + [] CONN_B.receive(tr_MDCX) -> value mgcp_cmd { + CONN_B.send(f_build_mdcx_rsp(mgcp_cmd)); + } +*/ + } + + /* request LS release on "A" side; call continues to be locally switched */ + CONN_A.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_release_lcls))); + CONN_A.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_locally_switched))); + f_sleep(2.0); + + /* request LS release on "B" side; call LS is released */ + CONN_B.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_release_lcls))); + interleave { + [] CONN_A.receive(tr_MDCX) -> value mgcp_cmd { + CONN_A.send(f_build_mdcx_rsp(mgcp_cmd)); + } + [] CONN_B.receive(tr_MDCX) -> value mgcp_cmd { + CONN_B.send(f_build_mdcx_rsp(mgcp_cmd)); + } + [] CONN_B.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_no_longer_ls))); + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_no_longer_ls)); + } + + f_lcls_test_fini(); +} + +/* Establish LCLS "connect" followed by a SCCP-level release of one leg */ +testcase TC_lcls_connect_clear() runs on lcls_test_CT { + var TestHdlrParams pars_a := valueof(t_def_TestHdlrPars); + var TestHdlrParams pars_b; + var MSC_ConnHdlr vc_conn; + var MgcpCommand mgcp_cmd; + var RSL_Message rsl; + + f_lcls_init(); + + pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O)); + pars_a.lcls.cfg := LCLS_CFG_both_way; + pars_a.lcls.csc := LCLS_CSC_connect; + pars_b := pars_a; + + /* first call is not possible to be LS (no second leg yet) */ + pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls; + /* second call should then reuslt in LS */ + pars_b.lcls.exp_sts := LCLS_STS_locally_switched; + + /* Expect LS to be established successfully */ + f_lcls_test_init(pars_a, pars_b); + CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + interleave { + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)); + [] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched)); + [] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL); + [] CONN_A.receive(tr_MDCX) -> value mgcp_cmd { + CONN_A.send(f_build_mdcx_rsp(mgcp_cmd)); + } + } + + /* Perform hard BSSMAP Clear on "A" side, expect no LS on "B" side */ + var myBSSMAP_Cause cause_val := GSM0808_CAUSE_CALL_CONTROL; + var octetstring l3_rr_chan_rel := '060D00'O; + CONN_A.send(ts_BSSMAP_ClearCommand(enum2int(cause_val))); + interleave { + [] CONN_A.receive(tr_RSL_DATA_REQ(?, tr_RslLinkID_DCCH(0), l3_rr_chan_rel)); + [] CONN_A.receive(tr_RSL_DEACT_SACCH(?)); + [] CONN_A.receive(tr_RSL_RF_CHAN_REL(?)) -> value rsl { + var RSL_IE_Body ieb; + f_rsl_find_ie(rsl, RSL_IE_CHAN_NR, ieb); + CONN_A.send(ts_RSL_RF_CHAN_REL_ACK(ieb.chan_nr)); + } + [] CONN_A.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); + [] CONN_A.receive(tr_BSSMAP_ClearComplete); + [] CONN_B.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_possible_ls)); + } + f_sleep(2.0); + + f_lcls_test_fini(); +} + + + + +/* TODO: + * verify IP/Port information in LCLS-triggered MDCX + * establish with one side connect, then enable using LCLS CTRL + * LCLS CTRL for call that doesn't have LCLS enabled + * LCLS IEs without GCR in ASS CMD + * GCR updates? + * Handover related LCLS bits (after we have inter-BSC HO in OsmoBSC) +*/ + + +control { + + execute( TC_lcls_gcr_only() ); + execute( TC_lcls_gcr_bway_connect() ); + execute( TC_lcls_gcr_nomatch_bway_connect() ); + execute( TC_lcls_gcr_bway_dont_connect() ); + execute( TC_lcls_gcr_unsuppported_cfg() ); + execute( TC_lcls_gcr_unsuppported_csc() ); + execute( TC_lcls_gcr_bway_dont_connect_csc() ); + execute( TC_lcls_connect_break() ); + execute( TC_lcls_connect_clear() ); + + +} + + +} diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn index 16a0547d..225e86b8 100644 --- a/bsc/MSC_ConnectionHandler.ttcn +++ b/bsc/MSC_ConnectionHandler.ttcn @@ -356,6 +356,15 @@ template (value) TestHdlrEncrParams t_EncrParams(OCT1 alg, octetstring key) := { enc_key := key } +type record TestHdlrParamsLcls { + GlobalCallReferenceValue gcr optional, + /* LCLS Configuration */ + BIT4 cfg optional, + /* LCLS Connection Status Control */ + BIT4 csc optional, + BIT4 exp_sts optional +} + type record TestHdlrParams { OCT1 ra, GsmFrameNumber fn, @@ -363,7 +372,8 @@ type record TestHdlrParams { RslLinkId link_id, integer media_nr, /* determins MGCP EP, port numbers */ BSSMAP_IE_SpeechCodecList ass_codec_list optional, - TestHdlrEncrParams encr optional + TestHdlrEncrParams encr optional, + TestHdlrParamsLcls lcls }; template (value) TestHdlrParams t_def_TestHdlrPars := { @@ -373,7 +383,13 @@ template (value) TestHdlrParams t_def_TestHdlrPars := { link_id := valueof(ts_RslLinkID_DCCH(0)), media_nr := 1, ass_codec_list := omit, - encr := omit + encr := omit, + lcls := { + gcr := omit, + cfg := omit, + csc := omit, + exp_sts := omit + } } function f_create_chan_and_exp() runs on MSC_ConnHdlr { @@ -711,11 +727,36 @@ return boolean { } } +/* patch an BSSMAP ASS REQ with LCLS related IEs, depending on g_params */ +function f_ass_patch_lcls(inout template (omit) PDU_BSSAP ass_tpl, + inout template PDU_BSSAP ass_cpl) runs on MSC_ConnHdlr { + if (istemplatekind(ass_tpl, "omit")) { + return; + } + if (ispresent(g_pars.lcls.gcr)) { + ass_tpl.pdu.bssmap.assignmentRequest.globalCallReference := ts_BSSMAP_IE_GCR(g_pars.lcls.gcr); + } + if (ispresent(g_pars.lcls.cfg)) { + ass_tpl.pdu.bssmap.assignmentRequest.lCLS_Configuration := ts_BSSMAP_IE_LclsCfg(g_pars.lcls.cfg); + } + if (ispresent(g_pars.lcls.csc)) { + ass_tpl.pdu.bssmap.assignmentRequest.lCLS_ConnectionStatusControl := ts_BSSMAP_IE_LclsCsc(g_pars.lcls.csc); + } + if (ispresent(g_pars.lcls.exp_sts)) { + ass_cpl.pdu.bssmap.assignmentComplete.lCLS_BSS_Status := tr_BSSMAP_IE_LclsSts(g_pars.lcls.exp_sts); + } else { + ass_cpl.pdu.bssmap.assignmentComplete.lCLS_BSS_Status := omit; + } +} + /* establish a channel fully, expecting an assignment matching 'exp' */ function f_establish_fully(template (omit) PDU_BSSAP ass_tpl, template PDU_BSSAP exp_ass_cpl) runs on MSC_ConnHdlr { f_MscConnHdlr_init(g_pars.media_nr, "127.0.0.2", "127.0.0.3"); + /* patch in the LCLS related items, as needed */ + f_ass_patch_lcls(ass_tpl, exp_ass_cpl); + f_create_chan_and_exp(); /* we should now have a COMPL_L3 at the MSC */ BSSAP.receive(tr_BSSMAP_ComplL3); diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn index ad90d66c..d92ea612 100644 --- a/library/BSSMAP_Templates.ttcn +++ b/library/BSSMAP_Templates.ttcn @@ -1136,6 +1136,18 @@ modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP tr_BSSMAP_LclsNotificationSts(BIT4 sts) +modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + lCLS_Notification := { + messageType := '76'O, + lCLS_BSS_Status := tr_BSSMAP_IE_LclsSts(sts), + lCLS_BreakRequest := omit + } + } + } +} -- cgit v1.2.3