From 261af4b501b063e2ca190b40b8262200f866a98b Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 12 Feb 2018 21:20:39 +0100 Subject: [PATCH] WIP: Intra-BSC handover testing Change-Id: Ic47e639a7c8640c736c84a44780fc8e111a64b52 --- bsc/BSC_Tests.ttcn | 68 +++++++++++++++++++ bsc/MSC_ConnectionHandler.ttcn | 119 ++++++++++++++++++++++++++++++++- library/L3_Templates.ttcn | 19 ++++++ 3 files changed, 203 insertions(+), 3 deletions(-) diff --git a/bsc/BSC_Tests.ttcn b/bsc/BSC_Tests.ttcn index 9ab3b2c60..cccec496e 100644 --- a/bsc/BSC_Tests.ttcn +++ b/bsc/BSC_Tests.ttcn @@ -1277,6 +1277,8 @@ function f_start_handler(void_fn fn, charstring id) runs on test_CT return MSC_C connect(vc_conn:MGCP_PROC, vc_MGCP:MGCP_PROC); connect(vc_conn:RSL, bts[0].rsl.vc_RSL:CLIENT_PT); connect(vc_conn:RSL_PROC, bts[0].rsl.vc_RSL:RSL_PROC); + connect(vc_conn:RSL1, bts[1].rsl.vc_RSL:CLIENT_PT); + connect(vc_conn:RSL1_PROC, bts[1].rsl.vc_RSL:RSL_PROC); connect(vc_conn:BSSAP, g_bssap.vc_BSSMAP:CLIENT); connect(vc_conn:MGCP, vc_MGCP:MGCP_CLIENT); vc_conn.start(derefers(fn)(id)); @@ -1553,6 +1555,71 @@ testcase TC_err_84_unknown_msg() runs on test_CT { vc_conn.done; } +/* execute a "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7>" command on given Dchan */ +private function f_vty_ss_action(charstring suffix, integer bts_nr, integer trx_nr, RslChannelNr chan_nr) +runs on MSC_ConnHdlr { + /* FIXME: resolve those from component-global state */ + var integer ts_nr := chan_nr.tn; + var integer ss_nr; + if (ischosen(chan_nr.u.ch0)) { + ss_nr := 0; + } else if (ischosen(chan_nr.u.lm)) { + ss_nr := chan_nr.u.lm.sub_chan; + } else if (ischosen(chan_nr.u.sdcch4)) { + ss_nr := chan_nr.u.sdcch4.sub_chan; + } else if (ischosen(chan_nr.u.sdcch8)) { + ss_nr := chan_nr.u.sdcch8.sub_chan; + } else { + setverdict(fail, "Invalid ChanNr ", chan_nr); + self.stop; + } + + var charstring cmd := "bts "&int2str(bts_nr)&" trx "&int2str(trx_nr)& + " timeslot "&int2str(ts_nr)&" sub-slot "&int2str(ss_nr)&" "; + f_vty_transceive(BSCVTY, cmd & suffix); +} + +private function f_vty_handover(integer bts_nr, integer trx_nr, RslChannelNr chan_nr, + integer new_bts_nr) +runs on MSC_ConnHdlr { + f_vty_ss_action("handover " & int2str(new_bts_nr), bts_nr, trx_nr, chan_nr); +} + +/* intra-BSC hand-over between BTS0 and BTS1 */ +private function f_tc_ho_int(charstring id) runs on MSC_ConnHdlr { + var TestHdlrParams pars := valueof(t_def_TestHdlrPars); + var template PDU_BSSAP exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?); + var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(ts_BSSMAP_IE_AoIP_TLA4('01020304'O, 2342)); + var PDU_BSSAP ass_cmd := valueof(ts_BSSMAP_AssignmentReq(omit, tla)); + const OCT8 kc := '0001020304050607'O; + + ass_cmd.pdu.bssmap.assignmentRequest.channelType := valueof(ts_BSSMAP_IE_ChannelType); + ass_cmd.pdu.bssmap.assignmentRequest.codecList := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR})); + + f_establish_fully(pars, ass_cmd, exp_compl); + + var HandoverState hs := { + rr_ho_cmpl_seen := false, + handover_done := false, + old_chan_nr := - + }; + /* issue hand-over command on VTY */ + f_vty_handover(0, 0, g_chan_nr, 1); + /* temporarily suspend DChan processing on BTS1 to avoid race with RSLEM_register */ + f_rslem_suspend(RSL1_PROC); + alt { + [] as_handover(hs); + /* FIXME: somehow determine that the hand-over has completed, by MGCP MDCX? */ + } +} + +testcase TC_ho_int() runs on test_CT { + var MSC_ConnHdlr vc_conn; + f_init(2, true); + f_sleep(1.0); + vc_conn := f_start_handler(refers(f_tc_ho_int), testcasename()); + vc_conn.done; +} control { @@ -1623,6 +1690,7 @@ control { execute( TC_unsol_ho_fail() ); execute( TC_err_82_short_msg() ); execute( TC_err_84_unknown_msg() ); + execute( TC_ho_int() ); } } diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn index 5f1095a83..b68b4f80c 100644 --- a/bsc/MSC_ConnectionHandler.ttcn +++ b/bsc/MSC_ConnectionHandler.ttcn @@ -62,7 +62,8 @@ type record BtsMediaState { type record MediaState { MgcpEndpoint mgcp_ep, MgcpConnState mgcp_conn[2], - BtsMediaState bts + BtsMediaState bts, + BtsMediaState bts1 /* only during hand-over */ }; function f_MediaState_init(inout MediaState g_media, integer nr, HostName bts, HostName mgw) { @@ -78,6 +79,17 @@ function f_MediaState_init(inout MediaState g_media, integer nr, HostName bts, H peer := - } + g_media.bts1 := { + ipa_crcx_seen := false, + conn_id := nr, + rtp_pt := 0, + bts := { + host := bts, /* FIXME */ + port_nr := 9000 + nr*2 + }, + peer := - + } + g_media.mgcp_ep := "rtpbridge/" & int2str(nr) & "@mgw"; for (var integer i:= 0; i < sizeof(g_media.mgcp_conn); i := i+1) { @@ -166,9 +178,54 @@ altstep as_Media() runs on MSC_ConnHdlr { oct2int(f_inet_addr(g_media.bts.peer.host)), g_media.bts.peer.port_nr, g_media.bts.rtp_pt)); - //g_media.ipa_mdcx_seen := true; + //g_media.bts.ipa_mdcx_seen := true; repeat; } + + /* on second (new) BTS during hand-over */ + [not g_media.bts1.ipa_crcx_seen] RSL1.receive(tr_RSL_IPA_CRCX(g_chan_nr)) -> value rsl { + /* Extract parameters from request + use in response */ + if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD, ie)) { + g_media.bts1.rtp_pt := ie.ipa_rtp_pt; + } + if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) { + g_media.bts1.rtp_pt := ie.ipa_rtp_pt2; + } + RSL1.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts1.conn_id, + oct2int(f_inet_addr(g_media.bts1.bts.host)), + g_media.bts1.bts.port_nr, + g_media.bts1.rtp_pt)); + g_media.bts1.ipa_crcx_seen := true; + repeat; + } + /* on second (new) BTS during hand-over */ + [g_media.bts1.ipa_crcx_seen] RSL1.receive(tr_RSL_IPA_MDCX(g_chan_nr, ?)) -> value rsl{ + /* Extract conn_id, ip, port, rtp_pt2 from request + use in response */ + f_rsl_find_ie(rsl, RSL_IE_IPAC_CONN_ID, ie); + if (g_media.bts1.conn_id != ie.ipa_conn_id) { + setverdict(fail, "IPA MDCX for unknown ConnId", rsl); + self.stop; + } + /* mandatory */ + f_rsl_find_ie(rsl, RSL_IE_IPAC_REMOTE_IP, ie); + g_media.bts1.peer.host := f_inet_ntoa(int2oct(ie.ipa_remote_ip, 4)); + f_rsl_find_ie(rsl, RSL_IE_IPAC_REMOTE_PORT, ie); + g_media.bts1.peer.port_nr := ie.ipa_remote_port; + /* optional */ + if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD, ie)) { + g_media.bts1.rtp_pt := ie.ipa_rtp_pt; + } + if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) { + g_media.bts1.rtp_pt := ie.ipa_rtp_pt2; + } + RSL1.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts1.conn_id, + oct2int(f_inet_addr(g_media.bts1.peer.host)), + g_media.bts1.peer.port_nr, + g_media.bts1.rtp_pt)); + //g_media.bts1.ipa_mdcx_seen := true; + repeat; + } + [] MGCP.receive(tr_CRCX) -> value mgcp_cmd { var SDP_Message sdp; var integer cid := f_get_free_mgcp_conn(); @@ -219,7 +276,7 @@ altstep as_Media() runs on MSC_ConnHdlr { int2str(mgcp_conn.sample_rate))), valueof(ts_SDP_ptime(mgcp_conn.ptime)) } )); MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, sdp)); - //mgcp_mdcx_seen := true; + //g_media.mgcp_mdcx_seen := true; repeat; } } @@ -635,5 +692,61 @@ runs on MSC_ConnHdlr return PDU_BSSAP { return bssap; } +type record HandoverState { + /* Assignment related bits */ + boolean rr_ho_cmpl_seen, + boolean handover_done, + RslChannelNr old_chan_nr +}; + +altstep as_handover(inout HandoverState st) runs on MSC_ConnHdlr { + var RSL_Message rsl; + [not st.rr_ho_cmpl_seen] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr)) -> value rsl { + var PDU_ML3_NW_MS l3 := dec_PDU_ML3_NW_MS(rsl.ies[2].body.l3_info.payload); + log("Rx L3 from net: ", l3); + if (ischosen(l3.msgs.rrm.handoverCommand)) { + var RslChannelNr new_chan_nr; + var GsmArfcn arfcn; + f_ChDesc2RslChanNr(l3.msgs.rrm.handoverCommand.channelDescription2, + new_chan_nr, arfcn); + /* FIXME: Determine TRX NR by ARFCN, instead of hard-coded TRX0! */ + + /* register our component for this channel number at the RSL Emulation */ + f_rslem_register(0, new_chan_nr, RSL1_PROC); + + /* resume processing of RSL DChan messages, which was temporarily suspended + * before performing a hand-over */ + f_rslem_resume(RSL1_PROC); + + /* send handover complete over the new channel */ + var PDU_ML3_MS_NW l3_tx := valueof(ts_RRM_HandoverComplete('00'O)); + RSL1.send(ts_RSL_DATA_IND(new_chan_nr, valueof(ts_RslLinkID_DCCH(0)), + enc_PDU_ML3_MS_NW(l3_tx))); + /* by default, send via the new channel from now */ + st.old_chan_nr := g_chan_nr; + g_chan_nr := new_chan_nr; + st.rr_ho_cmpl_seen := true; + repeat; + } else { + setverdict(fail, "Unexpected L3 received", l3); + self.stop; + } + } + [st.rr_ho_cmpl_seen] as_Media(); + [st.rr_ho_cmpl_seen] RSL.receive(tr_RSL_REL_REQ(st.old_chan_nr, tr_RslLinkID_DCCH(0))) { + RSL.send(ts_RSL_REL_CONF(st.old_chan_nr, valueof(ts_RslLinkID_DCCH(0)))); + repeat; + } + [st.rr_ho_cmpl_seen] RSL.receive(tr_RSL_RF_CHAN_REL(st.old_chan_nr)) { + RSL.send(ts_RSL_RF_CHAN_REL_ACK(st.old_chan_nr)); + /* unregister for old channel number in RSL emulation */ + /* FIXME: Determine TRX NR by ARFCN, instead of hard-coded TRX0! */ + f_rslem_unregister(0, st.old_chan_nr); + st.handover_done := true; + repeat; + } +} + + } diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn index d2ee605f7..cfbf96b37 100644 --- a/library/L3_Templates.ttcn +++ b/library/L3_Templates.ttcn @@ -338,6 +338,25 @@ template (value) PDU_ML3_MS_NW ts_RRM_HandoverFailure(OCT1 cause) := { } } +template (value) PDU_ML3_MS_NW ts_RRM_HandoverComplete(OCT1 cause) := { + discriminator := '0000'B, /* overwritten */ + tiOrSkip := { + skipIndicator := '0000'B + }, + msgs := { + rrm := { + handoverComplete := { + messageType := '00101100'B, + rRCause := { + valuePart := cause + }, + mobileObsservedTimeDiff := omit, + mobileTimeDifferenceHyperframe := omit + } + } + } +} + function ts_CM3_TLV(template (omit) OCTN cm3) return template MobileStationClassmark3_TLV { if (not isvalue(cm3)) { return omit;