diff options
Diffstat (limited to 'bsc/MSC_ConnectionHandler.ttcn')
-rw-r--r-- | bsc/MSC_ConnectionHandler.ttcn | 768 |
1 files changed, 609 insertions, 159 deletions
diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn index 5460e2cb..88a1b58f 100644 --- a/bsc/MSC_ConnectionHandler.ttcn +++ b/bsc/MSC_ConnectionHandler.ttcn @@ -32,6 +32,7 @@ import from MGCP_Types all; import from MGCP_Templates all; import from MGCP_Emulation all; import from SDP_Types all; +import from SDP_Templates all; import from StatsD_Checker all; @@ -45,12 +46,16 @@ import from L3_Templates all; import from TELNETasp_PortType all; import from Osmocom_VTY_Functions all; +import from TCCConversion_Functions all; /*********************************************************************** * Media related handling ***********************************************************************/ +/* TODO: import OSMUX_Types.ttcn */ +type INT1 OsmuxCID (0 .. 255); + /* Get the matching payload type for a specified BSSAP codec type * (see also: BSSAP_Types.ttcn */ private function f_get_mgcp_pt(BSSMAP_FIELD_CodecType codecType) return SDP_FIELD_PayloadType { @@ -87,7 +92,9 @@ type record MgcpConnState { integer ptime, /* 20 */ uint7_t rtp_pt, /* RTP Payload Type */ HostPort mgw, /* MGW side */ - HostPort peer /* CA side */ + HostPort peer, /* CA side */ + OsmuxCID local_osmux_cid optional, + OsmuxCID remote_osmux_cid optional }; /* BTS media state */ @@ -97,7 +104,9 @@ type record BtsMediaState { uint16_t conn_id, uint7_t rtp_pt, HostPort bts, - HostPort peer + HostPort peer, + OsmuxCID local_osmux_cid optional, + OsmuxCID remote_osmux_cid optional }; type record MediaState { @@ -118,7 +127,9 @@ function f_MediaState_init(inout MediaState g_media, integer nr, HostName bts, H host := bts, port_nr := 9000 + nr*2 }, - peer := - + peer := -, + local_osmux_cid := nr, + remote_osmux_cid := omit } g_media.bts1 := { @@ -130,7 +141,9 @@ function f_MediaState_init(inout MediaState g_media, integer nr, HostName bts, H host := bts, /* FIXME */ port_nr := 9000 + nr*2 }, - peer := - + peer := -, + local_osmux_cid := nr, + remote_osmux_cid := omit } g_media.mgcp_ep := "rtpbridge/" & int2str(nr) & "@mgw"; @@ -145,6 +158,8 @@ function f_MediaState_init(inout MediaState g_media, integer nr, HostName bts, H g_media.mgcp_conn[i].crcx_seen_exp := 0; g_media.mgcp_conn[i].mdcx_seen_exp := 0; g_media.mgcp_conn[i].conn_id := f_mgcp_alloc_conn_id(); + g_media.mgcp_conn[i].local_osmux_cid := i; + g_media.mgcp_conn[i].remote_osmux_cid := omit; } g_media.mgcp_conn[0].mgw := { @@ -184,13 +199,35 @@ private function f_get_mgcp_conn(MgcpConnectionId cid) runs on MSC_ConnHdlr retu return -1; } +/* Verify that CSD CRCX/MDCX has the RSL_IE_IPAC_RTP_CSD_FMT IE, and that + * inside it the D value is set to RSL_IPA_RTP_CSD_TRAU_BTS. */ +private function f_ipacc_crcx_mdcx_check_rtp_pt_csd(RSL_Message rsl) runs on MSC_ConnHdlr { + var SDP_FIELD_PayloadType pt_csd := PT_CSD; + var RSL_IE_Body ie; + + if (g_media.bts.rtp_pt != enum2int(pt_csd)) { + return; + } + + if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_CSD_FMT, ie)) { + if (ie.ipa_rtp_csd_fmt.d != RSL_IPA_RTP_CSD_TRAU_BTS) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Rx unexpected IPAC CRCX for CSD with RTP_CSD_FMT IE"); + } + return; + } + + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Rx unexpected IPAC CRCX for CSD without RTP_CSD_FMT IE"); +} + /* altstep for handling of IPACC media related commands. Activated by as_Media() to test * RSL level media handling */ -altstep as_Media_ipacc() runs on MSC_ConnHdlr { +altstep as_Media_ipacc(RSL_DCHAN_PT rsl_pt := RSL, RSL_DCHAN_PT rsl_pt_ho_target := RSL1) runs on MSC_ConnHdlr { var RSL_Message rsl; var RSL_IE_Body ie; var boolean b_unused; - [not g_media.bts.ipa_crcx_seen] RSL.receive(tr_RSL_IPA_CRCX(g_chan_nr)) -> value rsl { + [not g_media.bts.ipa_crcx_seen] rsl_pt.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.bts.rtp_pt := ie.ipa_rtp_pt; @@ -198,14 +235,28 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) { g_media.bts.rtp_pt := ie.ipa_rtp_pt2; } - RSL.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts.conn_id, - oct2int(f_inet_addr(g_media.bts.bts.host)), + if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) { + if (not g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX with Osmux CID IE"); + } + g_media.bts.remote_osmux_cid := ie.osmux_cid.cid; + } else { + if (g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX without Osmux CID IE"); + } + g_media.bts.local_osmux_cid := omit; + g_media.bts.remote_osmux_cid := omit; + } + f_ipacc_crcx_mdcx_check_rtp_pt_csd(rsl); + rsl_pt.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts.conn_id, + f_inet_addr(g_media.bts.bts.host), g_media.bts.bts.port_nr, - g_media.bts.rtp_pt)); + g_media.bts.rtp_pt, + g_media.bts.local_osmux_cid)); g_media.bts.ipa_crcx_seen := true; repeat; } - [g_media.bts.ipa_crcx_seen] RSL.receive(tr_RSL_IPA_MDCX(g_chan_nr, ?)) -> value rsl{ + [g_media.bts.ipa_crcx_seen] rsl_pt.receive(tr_RSL_IPA_MDCX(g_chan_nr, ?)) -> value rsl{ /* Extract conn_id, ip, port, rtp_pt2 from request + use in response */ b_unused := f_rsl_find_ie(rsl, RSL_IE_IPAC_CONN_ID, ie); if (g_media.bts.conn_id != ie.ipa_conn_id) { @@ -213,7 +264,7 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { } /* mandatory */ b_unused := f_rsl_find_ie(rsl, RSL_IE_IPAC_REMOTE_IP, ie); - g_media.bts.peer.host := f_inet_ntoa(int2oct(ie.ipa_remote_ip, 4)); + g_media.bts.peer.host := f_inet_ntoa(ie.ipa_remote_ip); b_unused := f_rsl_find_ie(rsl, RSL_IE_IPAC_REMOTE_PORT, ie); g_media.bts.peer.port_nr := ie.ipa_remote_port; /* optional */ @@ -223,16 +274,30 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) { g_media.bts.rtp_pt := ie.ipa_rtp_pt2; } - RSL.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts.conn_id, - oct2int(f_inet_addr(g_media.bts.peer.host)), + if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) { + if (not g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX with Osmux CID IE"); + } + g_media.bts.remote_osmux_cid := ie.osmux_cid.cid; + } else { + if (g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX without Osmux CID IE"); + } + g_media.bts.local_osmux_cid := omit; + g_media.bts.remote_osmux_cid := omit; + } + f_ipacc_crcx_mdcx_check_rtp_pt_csd(rsl); + rsl_pt.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts.conn_id, + f_inet_addr(g_media.bts.peer.host), g_media.bts.peer.port_nr, - g_media.bts.rtp_pt)); + g_media.bts.rtp_pt, + g_media.bts.local_osmux_cid)); 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 { + [not g_media.bts1.ipa_crcx_seen] rsl_pt_ho_target.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; @@ -240,15 +305,28 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { 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)), + if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) { + if (not g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX with Osmux CID IE"); + } + g_media.bts.remote_osmux_cid := ie.osmux_cid.cid; + } else { + if (g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX without Osmux CID IE"); + } + g_media.bts.local_osmux_cid := omit; + g_media.bts.remote_osmux_cid := omit; + } + rsl_pt_ho_target.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts1.conn_id, + f_inet_addr(g_media.bts1.bts.host), g_media.bts1.bts.port_nr, - g_media.bts1.rtp_pt)); + g_media.bts1.rtp_pt, + g_media.bts.local_osmux_cid)); 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{ + [g_media.bts1.ipa_crcx_seen] rsl_pt_ho_target.receive(tr_RSL_IPA_MDCX(g_chan_nr, ?)) -> value rsl{ /* Extract conn_id, ip, port, rtp_pt2 from request + use in response */ b_unused := f_rsl_find_ie(rsl, RSL_IE_IPAC_CONN_ID, ie); if (g_media.bts1.conn_id != ie.ipa_conn_id) { @@ -256,7 +334,7 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { } /* mandatory */ b_unused := 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)); + g_media.bts1.peer.host := f_inet_ntoa(ie.ipa_remote_ip); b_unused := f_rsl_find_ie(rsl, RSL_IE_IPAC_REMOTE_PORT, ie); g_media.bts1.peer.port_nr := ie.ipa_remote_port; /* optional */ @@ -266,10 +344,23 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { 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)), + if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) { + if (not g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX with Osmux CID IE"); + } + g_media.bts.remote_osmux_cid := ie.osmux_cid.cid; + } else { + if (g_pars.use_osmux_bts) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX without Osmux CID IE"); + } + g_media.bts.local_osmux_cid := omit; + g_media.bts.remote_osmux_cid := omit; + } + rsl_pt_ho_target.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts1.conn_id, + f_inet_addr(g_media.bts1.peer.host), g_media.bts1.peer.port_nr, - g_media.bts1.rtp_pt)); + g_media.bts1.rtp_pt, + g_media.bts.local_osmux_cid)); g_media.bts1.ipa_mdcx_seen := true; repeat; } @@ -279,10 +370,10 @@ altstep as_Media_ipacc() runs on MSC_ConnHdlr { function f_rx_crcx(MgcpCommand mgcp_cmd) runs on MSC_ConnHdlr return template MgcpResponse { - var MgcpOsmuxCID osmux_cid; var SDP_Message sdp; var integer cid := f_get_free_mgcp_conn(); var charstring local_rtp_addr; + var MgcpOsmuxCID osmux_cid; if (g_pars.media_mgw_offer_ipv6 == true) { local_rtp_addr := host_mgw_rtp_v6; /* Use IPv6 by default if no remote addr is provided by client */ @@ -317,9 +408,13 @@ function f_rx_crcx(MgcpCommand mgcp_cmd) int2str(mgcp_conn.sample_rate))), valueof(ts_SDP_ptime(mgcp_conn.ptime)) } )); var template MgcpResponse mgcp_resp; - if (g_pars.use_osmux and f_MgcpCmd_contains_par(mgcp_cmd, "X-OSMUX")) { + if ((g_pars.use_osmux_cn or g_pars.use_osmux_bts) and + f_MgcpCmd_contains_par(mgcp_cmd, "X-OSMUX")) { osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd); - mgcp_resp := ts_CRCX_ACK_osmux(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, osmux_cid, sdp); + if (osmux_cid != -1) { + mgcp_conn.remote_osmux_cid := osmux_cid; + } + mgcp_resp := ts_CRCX_ACK_osmux(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, mgcp_conn.local_osmux_cid, sdp); } else { mgcp_resp := ts_CRCX_ACK(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, sdp); } @@ -382,6 +477,9 @@ altstep as_Media_mgw(boolean norepeat := false) runs on MSC_ConnHdlr { var template MgcpMessage msg_mdcx := { command := tr_MDCX } + var template MgcpMessage msg_dlcx := { + command := tr_DLCX + } var template MgcpMessage msg_resp; [g_pars.aoip] MGCP.receive(tr_CRCX) -> value mgcp_cmd { @@ -403,7 +501,7 @@ altstep as_Media_mgw(boolean norepeat := false) runs on MSC_ConnHdlr { } } - [g_pars.aoip] MGCP.receive(tr_MDCX) -> value mgcp_cmd { + [g_pars.aoip and not g_pars.ignore_mgw_mdcx] MGCP.receive(tr_MDCX) -> value mgcp_cmd { mgcp_resp := f_rx_mdcx(mgcp_cmd); MGCP.send(mgcp_resp); if(norepeat == false) { @@ -411,7 +509,8 @@ altstep as_Media_mgw(boolean norepeat := false) runs on MSC_ConnHdlr { } } - [not g_pars.aoip] MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_mdcx)) -> value mrf { + [not g_pars.aoip and not g_pars.ignore_mgw_mdcx] + MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_mdcx)) -> value mrf { mgcp_resp := f_rx_mdcx(mrf.msg.command); msg_resp := { response := mgcp_resp @@ -421,15 +520,28 @@ altstep as_Media_mgw(boolean norepeat := false) runs on MSC_ConnHdlr { repeat; } } + + [g_pars.fail_on_dlcx and g_pars.aoip] MGCP.receive(tr_DLCX) { + setverdict(fail, "Unexpected DLCX received"); + } + + [g_pars.fail_on_dlcx and not g_pars.aoip] MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_dlcx)) { + setverdict(fail, "Unexpected DLCX received"); + } } /* Altsteps for handling of media related commands. Can be activated by a given * test case if it expects to see media related handling (i.e. voice calls) */ altstep as_Media() runs on MSC_ConnHdlr { - [] as_Media_ipacc(); + [not g_pars.ignore_ipa_media] as_Media_ipacc(); [] as_Media_mgw(); } +type port Coord_PT message +{ + inout charstring; +} with { extension "internal" }; + /* this component represents a single subscriber connection at the MSC. * There is a 1:1 mapping between SCCP connections and RAN_ConnHdlr components. * We inherit all component variables, ports, functions, ... from RAN_ConnHdlr */ @@ -440,6 +552,8 @@ type component MSC_ConnHdlr extends RAN_ConnHdlr, RSL_DchanHdlr, MGCP_ConnHdlr, /* procedure port back to our parent (RAN_Emulation_CT) for control */ port RAN_PROC_PT RAN; port TELNETasp_PT BSCVTY; + port Coord_PT COORD; + port Coord_PT COORD2; /* Proxy MGCP-over-IPA and MGCP-over-UDP */ port IPA_MGCP_PT MGCP_MSC_CLIENT; @@ -468,6 +582,7 @@ function f_MscConnHdlr_init_vty() runs on MSC_ConnHdlr { /* initialize all parameters */ function f_MscConnHdlr_init(integer i, HostName bts, HostName mgw, BSSMAP_FIELD_CodecType codecType) runs on MSC_ConnHdlr { + g_trans_id := i * 1000; /* Avoid different MscConnHdlr submitting same trans_id over MGCP-IPA */ f_MediaState_init(g_media, i, bts, mgw, codecType); f_MscConnHdlr_init_vty(); } @@ -516,6 +631,7 @@ const RanOps MSC_RanOps := { protocol := RAN_PROTOCOL_BSSAP, transport := BSSAP_TRANSPORT_AoIP, use_osmux := false, + bssap_reset_retries := 1, sccp_addr_local := omit, sccp_addr_peer := omit } @@ -542,13 +658,83 @@ function f_create_bssmap_exp(octetstring l3_enc) runs on MSC_ConnHdlr { } type record TestHdlrEncrParams { - OCT1 enc_alg, - octetstring enc_key + /* A mask of multiple encryption algorithms, for Encryption Information IE. */ + OCT1 enc_alg_permitted, + /* Expect this encryption algorithm in Channel Activation from the BSC. + * To expect no encryption, set to '01'O == A5/0. */ + OCT1 enc_alg_expect, + /* In BSSMAP, the Chosen Encryption Algorithm IE that the test sends to the BSC. + * When set to omit, the MSC will not send a Chosen Encryption Algorithm IE. */ + OCT1 enc_alg_chosen optional, + octetstring enc_key, + octetstring enc_kc128 optional }; -template (value) TestHdlrEncrParams t_EncrParams(OCT1 alg, octetstring key) := { - enc_alg := alg, - enc_key := key +template (value) TestHdlrEncrParams t_EncrParams(OCT1 alg_permitted, + octetstring key, template (omit) octetstring kc128 := omit) := { + enc_alg_permitted := alg_permitted, + enc_alg_expect := alg_permitted, /* <- for compat with tests before enc_alg_expect was added */ + enc_alg_chosen := alg_permitted, /* <- for compat with tests before enc_alg_chosen was added */ + enc_key := key, + enc_kc128 := kc128 +} + +template (value) TestHdlrEncrParams t_EncrParams2(OCT1 alg_permitted, + OCT1 alg_expect, + template(omit) OCT1 alg_chosen, + octetstring key, + template (omit) octetstring kc128 := omit) := { + enc_alg_permitted := alg_permitted, + enc_alg_expect := alg_expect, + enc_alg_chosen := alg_chosen, + enc_key := key, + enc_kc128 := kc128 +} + +/* Convenience for common t_EncrParams2 usage. + * + * To test a scenario where only A5/3 is permitted: + * f_encr_params('08'O); + * To test a scenario where A5/3 is chosen from A5/0,A5/1,A5/3 (1 | 2 | 8 = 11 = 0xb): + * f_encr_params('0b'O, '08'O); + * To test the same, but the Chosen Encryption Algorithm IE should reflect A5/1: + * f_encr_params('0b'O, '08'O, '02'O); + * To test the same, but the MSC sends no Chosen Encryption Algorithm IE: + * f_encr_params('0b'O, '08'O, omit); + * + * Set alg_chosen and alg_expect magically when == '00'O: + * - enc_alg_expect is set to alg_chosen if present, else to alg_permitted. + * - enc_alg_chosen is set to alg_permitted. + * When only alg_permitted is given, alg_permitted must reflect only one algorithm, or f_cipher_mode_bssmap_to_rsl() + * will fail. + * When alg_chosen is passed as omit, the messages from the MSC will not contain a Chosen Encryption Algorithm IE. + */ +function f_encr_params(OCT1 alg_permitted, OCT1 alg_expect := '00'O, template (omit) OCT1 alg_chosen := '00'O, + boolean kc128 := false) + return TestHdlrEncrParams +{ + if (not istemplatekind(alg_chosen, "omit")) { + if (valueof(alg_chosen) == '00'O) { + if (alg_expect != '00'O) { + alg_chosen := alg_expect; + } else { + /* In this case, alg_permitted should have only one permitted algo */ + alg_chosen := alg_permitted; + } + } + if (alg_expect == '00'O) { + alg_expect := valueof(alg_chosen); + } + } + if (alg_expect == '00'O) { + /* In this case, alg_permitted should have only one permitted algo */ + alg_expect := valueof(alg_permitted); + } + var template (omit) octetstring kc := omit; + if (kc128) { + kc := f_rnd_octstring(16); + } + return valueof(t_EncrParams2(alg_permitted, alg_expect, alg_chosen, f_rnd_octstring(8), kc)); } type record TestHdlrParamsLcls { @@ -568,10 +754,26 @@ type record TestHdlrParamsMSCPool { PDU_ML3_MS_NW l3_info optional } +type record ASCITest { + boolean vgcs_setup_ok, + boolean vgcs_assign_ok, + boolean vgcs_assign_fail, + boolean vgcs_talker_req, + boolean vgcs_talker_fail, + boolean vgcs_talker_est, + boolean vgcs_talker_rel, + boolean vgcs_uplink_reject, + boolean vgcs_uplink_seized, + boolean vgcs_uplink_release, + boolean delay_bts, + boolean delay_msc +}; + type record TestHdlrParams { OCT1 ra, GsmFrameNumber fn, - hexstring imsi, + hexstring imsi optional, + hexstring imei optional, RslLinkId link_id, integer media_nr, /* determins MGCP EP, port numbers */ BSSMAP_IE_SpeechCodecList ass_codec_list optional, @@ -584,10 +786,24 @@ type record TestHdlrParams { uint5_t exp_ms_power_level, boolean exp_ms_power_params, boolean aoip, - boolean use_osmux, + boolean use_osmux_cn, + boolean use_osmux_bts, charstring host_aoip_tla, TestHdlrParamsMSCPool mscpool, - boolean media_mgw_offer_ipv6 + integer mgwpool_idx, /* MGCP_Emulation_CT (vc_MGCP) to use for this MSC ConnHdlr */ + boolean media_mgw_offer_ipv6, + OCT3 last_used_eutran_plmn optional, + boolean exp_fast_return, /* RR Release expected to contain CellSelectInd ? */ + boolean expect_channel_mode_modify, + uint3_t expect_tsc optional, + BSSMAP_IE_CellIdentifier cell_id_source, + boolean expect_ho_fail, + boolean expect_ho_fail_lchan_est, + boolean inter_bsc_ho_in__ho_req_in_initial_sccp_cr, + boolean ignore_mgw_mdcx, + boolean fail_on_dlcx, + boolean ignore_ipa_media, + ASCITest asci_test }; /* Note: Do not use valueof() to get a value of this template, use @@ -596,7 +812,8 @@ type record TestHdlrParams { template (value) TestHdlrParams t_def_TestHdlrPars := { ra := '23'O, fn := 23, - imsi := '001019876543210'H, + imsi := omit, /* set to random in f_gen_test_hdlr_pars() */ + imei := omit, /* set to random in f_gen_test_hdlr_pars() */ link_id := valueof(ts_RslLinkID_DCCH(0)), media_nr := 1, ass_codec_list := omit, @@ -615,38 +832,104 @@ template (value) TestHdlrParams t_def_TestHdlrPars := { exp_ms_power_level := 7, /* calculated from osmo-bsc.cfg "ms max power" */ exp_ms_power_params := false, aoip := true, - use_osmux := false, + use_osmux_cn := false, + use_osmux_bts := false, host_aoip_tla := "1.2.3.4", mscpool := { bssap_idx := 0, rsl_idx := 0, l3_info := omit }, - media_mgw_offer_ipv6 := true + mgwpool_idx := 0, + media_mgw_offer_ipv6 := true, + last_used_eutran_plmn := omit, + exp_fast_return := false, + expect_channel_mode_modify := false, + expect_tsc := omit, + cell_id_source := valueof(ts_CellID_LAC_CI(1, 1)), + expect_ho_fail := false, + expect_ho_fail_lchan_est := false, + inter_bsc_ho_in__ho_req_in_initial_sccp_cr := true, + ignore_mgw_mdcx := false, + fail_on_dlcx := true, + ignore_ipa_media := false, + asci_test := { + vgcs_setup_ok := false, + vgcs_assign_ok := false, + vgcs_assign_fail := false, + vgcs_talker_req := false, + vgcs_talker_fail := false, + vgcs_talker_est := false, + vgcs_talker_rel := false, + vgcs_uplink_reject := false, + vgcs_uplink_seized := false, + vgcs_uplink_release := false, + delay_bts := false, + delay_msc := false + } } -function f_create_chan_and_exp() runs on MSC_ConnHdlr { - var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); - var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); - var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3_info); +function f_create_chan_and_exp(template (present) PDU_BSSAP exp_l3_compl := ?) +runs on MSC_ConnHdlr { + var MobileIdentityLV mi; + var PDU_ML3_MS_NW l3_info; + var octetstring l3_enc; + var template uint3_t tsc := ?; + timer T; + + if (ispresent(g_pars.imsi)) { + mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + } else if (ispresent(g_pars.imei)) { + mi := valueof(ts_MI_IMEI_LV(g_pars.imei)); + } else { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Either imsi or imei must be set!"); + } + l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); + l3_enc := enc_PDU_ML3_MS_NW(l3_info); + + if (not istemplatekind(g_pars.expect_tsc, "omit")) { + tsc := g_pars.expect_tsc; + } + + if (istemplatekind(exp_l3_compl, "?")) { + if (g_pars.aoip == false) { + exp_l3_compl := tr_BSSMAP_ComplL3(l3_enc, codec_list := omit); + } else { + exp_l3_compl := tr_BSSMAP_ComplL3(l3_enc, codec_list := ?); + } + } - /* call helper function for CHAN_RQD -> IMM ASS ->EST_IND */ - RSL_Emulation.f_chan_est(g_pars.ra, l3_enc, g_pars.link_id, g_pars.fn); f_create_bssmap_exp(l3_enc); + /* call helper function for CHAN_RQD -> IMM ASS ->EST_IND */ + RSL_Emulation.f_chan_est(g_pars.ra, l3_enc, g_pars.link_id, g_pars.fn, tsc); + /* wait for a COMPL_L3 from the BSC to ensure that the SCCP connection is up */ + T.start(2.0); + alt { + [] BSSAP.receive(exp_l3_compl); + [] BSSAP.receive(tr_BSSMAP_ComplL3) { + setverdict(fail, "Received non-matching COMPLETE LAYER 3 INFORMATION"); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for COMPLETE LAYER 3 INFORMATION"); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + } + } } function f_rsl_send_l3(template PDU_ML3_MS_NW l3, template (omit) RslLinkId link_id := omit, - template (omit) RslChannelNr chan_nr := omit) runs on MSC_ConnHdlr { + template (omit) RslChannelNr chan_nr := omit, RSL_DCHAN_PT rsl_pt := RSL) runs on MSC_ConnHdlr { if (not isvalue(link_id)) { link_id := ts_RslLinkID_DCCH(0); } if (not isvalue(chan_nr)) { chan_nr := g_chan_nr; } - RSL.send(ts_RSL_DATA_IND(valueof(chan_nr), valueof(link_id), enc_PDU_ML3_MS_NW(valueof(l3)))); + rsl_pt.send(ts_RSL_DATA_IND(valueof(chan_nr), valueof(link_id), enc_PDU_ML3_MS_NW(valueof(l3)))); } -function f_rsl_reply(template PDU_ML3_MS_NW l3, RSL_Message orig) runs on MSC_ConnHdlr { +function f_rsl_reply(template PDU_ML3_MS_NW l3, RSL_Message orig, RSL_DCHAN_PT rsl_pt := RSL) runs on MSC_ConnHdlr { var RslChannelNr chan_nr := orig.ies[0].body.chan_nr; var RslLinkId link_id; if (orig.msg_type == RSL_MT_ENCR_CMD) { @@ -654,81 +937,138 @@ function f_rsl_reply(template PDU_ML3_MS_NW l3, RSL_Message orig) runs on MSC_Co } else { link_id := orig.ies[1].body.link_id; } - f_rsl_send_l3(l3, link_id, chan_nr); + f_rsl_send_l3(l3, link_id, chan_nr, rsl_pt := rsl_pt); } -/* Convert the chipher representation on BSSMAP to the representation used on RSL */ -function f_chipher_mode_bssmap_to_rsl(OCT1 alg_bssmap) return RSL_AlgId +/* Convert the cipher representation on BSSMAP to the representation used on RSL */ +function f_cipher_mode_bssmap_to_rsl(OCT1 alg_bssmap) return RSL_AlgId { - /* A5 0 */ - if (alg_bssmap == '01'O) { + select (alg_bssmap) { + case ('01'O) { return RSL_ALG_ID_A5_0; } + case ('02'O) { return RSL_ALG_ID_A5_1; } + case ('04'O) { return RSL_ALG_ID_A5_2; } + case ('08'O) { return RSL_ALG_ID_A5_3; } + case ('10'O) { return RSL_ALG_ID_A5_4; } + case ('20'O) { return RSL_ALG_ID_A5_5; } + case ('40'O) { return RSL_ALG_ID_A5_6; } + case ('80'O) { return RSL_ALG_ID_A5_7; } + case else { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected Encryption Algorithm: " & + oct2str(alg_bssmap)); return RSL_ALG_ID_A5_0; + } } - /* A5 1 */ - else if (alg_bssmap == '02'O) { - return RSL_ALG_ID_A5_1; - } - /* A5 2 */ - else if (alg_bssmap == '04'O) { - return RSL_ALG_ID_A5_2; - } - /* A5 3 */ - else if (alg_bssmap == '08'O) { - return RSL_ALG_ID_A5_3; +} + +/* Convert the cipher representation on BSSMAP to the one used on RR (3GPP TS 44.018) */ +function f_cipher_mode_bssmap_to_rr(OCT1 alg_bssmap) return BIT3 +{ + select (alg_bssmap) { + case ('01'O) /* A5/0 */ { return '000'B; } /* SC=0 */ + case ('02'O) /* A5/1 */ { return '000'B; } /* SC=1 */ + case ('04'O) /* A5/2 */ { return '001'B; } /* SC=1 */ + case ('08'O) /* A5/3 */ { return '010'B; } /* SC=1 */ + case ('10'O) /* A5/4 */ { return '011'B; } /* SC=1 */ + case ('20'O) /* A5/5 */ { return '100'B; } /* SC=1 */ + case ('40'O) /* A5/6 */ { return '101'B; } /* SC=1 */ + case ('80'O) /* A5/7 */ { return '110'B; } /* SC=1 */ + case else { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected Encryption Algorithm: " & + oct2str(alg_bssmap)); + return '000'B; + } } - /* A5 4 */ - else if (alg_bssmap == '10'O) { - return RSL_ALG_ID_A5_4; +} + +function f_verify_encr_info(RSL_Message rsl) runs on MSC_ConnHdlr { + var RSL_IE_Body encr_info; + var RSL_AlgId alg_rsl; + var template octetstring expect_kc; + + /* If no encryption is enabled, then make sure there is no RSL_IE_ENCR_INFO */ + if (not ispresent(g_pars.encr)) { + if (f_rsl_find_ie(rsl, RSL_IE_ENCR_INFO, encr_info)) { + setverdict(fail, "Found Encryption IE, but expected no encryption in ", rsl.msg_type); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + return; + } + setverdict(pass); + return; } - /* A5 5 */ - else if (alg_bssmap == '20'O) { - return RSL_ALG_ID_A5_5; + + /* RSL uses a different representation of the encryption algorithm, + * so we need to convert first */ + alg_rsl := f_cipher_mode_bssmap_to_rsl(g_pars.encr.enc_alg_expect); + + if (alg_rsl == RSL_ALG_ID_A5_4 and ispresent(g_pars.encr.enc_kc128)) { + expect_kc := g_pars.encr.enc_kc128; + } else if (alg_rsl == RSL_ALG_ID_A5_0) { + /* When A5/0 is chosen, no encryption is active, so technically, no key is needed. However, 3GPP TS + * 48.058 9.3.7 Encryption Information stays quite silent about presence or absence of a key for A5/0. + * The only thing specified is how to indicate the length of the key; the possibility that the key may + * be zero length is not explicitly mentioned. So it seems that we should always send the key along, + * even for A5/0. Still, let's also allow a zero length key for A5/0. */ + expect_kc := (g_pars.encr.enc_key, ''O); + } else { + expect_kc := g_pars.encr.enc_key; } - /* A5 6 */ - else if (alg_bssmap == '40'O) { - return RSL_ALG_ID_A5_6; + log("for encryption algo ", alg_rsl, " expect kc = ", expect_kc); + + if (not f_rsl_find_ie(rsl, RSL_IE_ENCR_INFO, encr_info)) { + if (alg_rsl == RSL_ALG_ID_A5_0) { + /* For A5/0, encryption is not active. It is fine to omit the Encryption Information in this + * case. Note that the first channel may see an RSL Encryption Command with A5/0 indicated, and + * a subsequent handover may activate a new channel without any Encryption Information. */ + setverdict(pass); + return; + } + setverdict(fail, "Missing Encryption Information IE in ", rsl.msg_type); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + return; } - /* A5 7 */ - else if (alg_bssmap == '80'O) { - return RSL_ALG_ID_A5_7; - } else { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected Encryption Algorithm"); - return RSL_ALG_ID_A5_0; + + if (not match(encr_info, tr_EncrInfo(alg_rsl, expect_kc))) { + setverdict(fail, "Unexpected Kc in Encryption Information IE in ", rsl.msg_type); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + return; } + setverdict(pass); } -function f_cipher_mode(OCT1 alg, OCT8 key, template OCT16 kc128 := omit, boolean exp_fail := false) +function f_cipher_mode(TestHdlrEncrParams enc, boolean exp_fail := false, + RSL_DCHAN_PT rsl_pt := RSL, RSLEM_PROC_PT rsl_proc_pt := RSL_PROC) runs on MSC_ConnHdlr { var PDU_BSSAP bssap; var RSL_Message rsl; - var RSL_AlgId alg_rsl; - if (isvalue(kc128)) { - BSSAP.send(ts_BSSMAP_CipherModeCmdKc128(alg, key, valueof(kc128))); + if (isvalue(enc.enc_kc128)) { + BSSAP.send(ts_BSSMAP_CipherModeCmdKc128(enc.enc_alg_permitted, enc.enc_key, valueof(enc.enc_kc128))); } else { - BSSAP.send(ts_BSSMAP_CipherModeCmd(alg, key)); + BSSAP.send(ts_BSSMAP_CipherModeCmd(enc.enc_alg_permitted, enc.enc_key)); } - /* RSL uses a different representation of the encryption algorithm, - * so we need to convert first */ - alg_rsl := f_chipher_mode_bssmap_to_rsl(alg); - alt { /* RSL/UE Side */ - [] RSL.receive(tr_RSL_ENCR_CMD(g_chan_nr, ?, alg_rsl, key)) -> value rsl { + [] rsl_pt.receive(tr_RSL_ENCR_CMD(g_chan_nr)) -> value rsl { var PDU_ML3_NW_MS l3 := dec_PDU_ML3_NW_MS(rsl.ies[3].body.l3_info.payload); log("Rx L3 from net: ", l3); + + f_verify_encr_info(rsl); + if (ischosen(l3.msgs.rrm.cipheringModeCommand)) { - f_rsl_reply(ts_RRM_CiphModeCompl, rsl); + f_rsl_reply(ts_RRM_CiphModeCompl, rsl, rsl_pt := rsl_pt); } repeat; } [] BSSAP.receive(tr_BSSMAP_CipherModeCompl) -> value bssap { - // bssap.bssmap.cipherModeComplete.chosenEncryptionAlgorithm.algoritmhIdentifier if (exp_fail == true) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected Cipher Mode Complete"); } else { setverdict(pass); + var RSL_AlgId alg_rsl := f_cipher_mode_bssmap_to_rsl(g_pars.encr.enc_alg_expect); + if (oct2int(bssap.pdu.bssmap.cipherModeComplete.chosenEncryptionAlgorithm.algorithmIdentifier) != enum2int(alg_rsl)) { + setverdict(fail, "Unexpected Encryption Algorithm ID in BSSMAP Cipher Mode Complete"); + } } } [] BSSAP.receive(tr_BSSMAP_CipherModeRej) -> value bssap { @@ -746,20 +1086,28 @@ function f_ChDesc2RslChanNr(ChannelDescription2_V ch_desc, out RslChannelNr chan var BIT5 inp := ch_desc.channelTypeandTDMAOffset; var uint3_t tn := bit2int(ch_desc.timeslotNumber); - if (match(inp, '00001'B)) { /* TCH/F */ + select (inp) { + case ('00001'B) { /* TCH/F */ chan_nr := valueof(t_RslChanNr_Bm(tn)); - } - else if (match(inp, '0001?'B)) { /* TCH/H */ + } + case ('11101'B) { /* VAMOS TCH/F */ + chan_nr := valueof(t_RslChanNr_Osmo_VAMOS_Bm(tn)); + } + case ('0001?'B) { /* TCH/H */ chan_nr := valueof(t_RslChanNr_Lm(tn, bit2int(substr(inp, 4, 1)))); - } - else if (match(inp, '001??'B)) { /* SDCCH/4 */ + } + case ('1111?'B) { /* VAMOS TCH/H */ + chan_nr := valueof(t_RslChanNr_Osmo_VAMOS_Lm(tn, bit2int(substr(inp, 4, 1)))); + } + case ('001??'B) { /* SDCCH/4 */ chan_nr := valueof(t_RslChanNr_SDCCH4(tn, bit2int(substr(inp, 3, 2)))); - } - else if (match(inp, '01???'B)) { /* SDCCH/8 */ + } + case ('01???'B) { /* SDCCH/8 */ chan_nr := valueof(t_RslChanNr_SDCCH8(tn, bit2int(substr(inp, 2, 3)))); - } - else { + } + case else { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unknown ChDesc!"); + } } if (ch_desc.octet3 and4b '10'O == '10'O) { @@ -772,7 +1120,7 @@ function f_ChDesc2RslChanNr(ChannelDescription2_V ch_desc, out RslChannelNr chan type record AssignmentState { /* global */ - boolean voice_call, + boolean rtp_stream, boolean is_assignment, /* Assignment related bits */ boolean rr_ass_cmpl_seen, @@ -781,19 +1129,23 @@ type record AssignmentState { boolean assignment_done, RslChannelNr old_chan_nr, /* Modify related bits */ + PDU_ML3_NW_MS rr_channel_mode_modify_msg optional, boolean rr_modify_seen, + RSL_Message rsl_mode_modify_msg optional, boolean modify_done } template (value) AssignmentState ts_AssignmentStateInit := { - voice_call := false, + rtp_stream := false, is_assignment := false, rr_ass_cmpl_seen := false, old_lchan_deact_sacch_seen := false, old_lchan_rll_rel_req_seen := false, assignment_done := false, old_chan_nr := -, + rr_channel_mode_modify_msg := omit, rr_modify_seen := false, + rsl_mode_modify_msg := omit, modify_done := false } @@ -811,13 +1163,20 @@ private function f_check_chan_act(AssignmentState st, RSL_Message chan_act) runs var RSL_IE_Body ms_power_param; var RSL_IE_Body ms_power; - if (ispresent(g_pars.encr) and g_pars.encr.enc_alg != '01'O) { + if (ispresent(g_pars.encr) and g_pars.encr.enc_alg_permitted != '01'O) { if (not f_rsl_find_ie(chan_act, RSL_IE_ENCR_INFO, encr_info)) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Missing Encryption IE in CHAN ACT"); } else { - var RSL_AlgId alg := f_chipher_mode_bssmap_to_rsl(g_pars.encr.enc_alg); - if (not match(encr_info, tr_EncrInfo(alg, g_pars.encr.enc_key))) { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Wrong Encryption IE in CHAN ACT"); + var RSL_AlgId alg := f_cipher_mode_bssmap_to_rsl(g_pars.encr.enc_alg_expect); + var octetstring expect_key; + if (alg == RSL_ALG_ID_A5_4) { + expect_key := g_pars.encr.enc_kc128; + } else { + expect_key := g_pars.encr.enc_key; + } + if (not match(encr_info, tr_EncrInfo(alg, expect_key))) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Unexpected Kc in Encryption IE in RSL ENCR CMD"); } } } else { @@ -844,33 +1203,51 @@ private function f_check_chan_act(AssignmentState st, RSL_Message chan_act) runs } -altstep as_assignment(inout AssignmentState st) runs on MSC_ConnHdlr { +function rr_chan_desc_tsc(ChannelDescription2_V cd2) + return uint3_t +{ + var uint3_t tsc := oct2int(cd2.octet3); + tsc := tsc / 32; /* shl 5 */ + return tsc; +} + +altstep as_assignment(inout AssignmentState st, RSL_DCHAN_PT rsl_pt := RSL, RSLEM_PROC_PT rsl_proc_pt := RSL_PROC) runs on MSC_ConnHdlr { var RSL_Message rsl; - [not st.rr_ass_cmpl_seen] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr)) -> value rsl { + [not st.rr_ass_cmpl_seen] rsl_pt.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.assignmentCommand)) { var RslChannelNr new_chan_nr; var GsmArfcn arfcn; + + if (not istemplatekind(g_pars.expect_tsc, "omit")) { + var uint3_t got_tsc := rr_chan_desc_tsc(l3.msgs.rrm.assignmentCommand.descrOf1stChAfterTime); + if (not match(got_tsc, g_pars.expect_tsc)) { + setverdict(fail, "RR Assignment: unexpected TSC in Channel Description: expected ", + g_pars.expect_tsc, " got ", got_tsc); + mtc.stop; + } + } + f_ChDesc2RslChanNr(l3.msgs.rrm.assignmentCommand.descrOf1stChAfterTime, 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); + f_rslem_register(0, new_chan_nr, PT := rsl_proc_pt); /* dispatch queued messages for this channel (if any) */ - f_rslem_dchan_queue_dispatch(); + f_rslem_dchan_queue_dispatch(PT := rsl_proc_pt); var PDU_ML3_MS_NW l3_tx := valueof(ts_RRM_AssignmentComplete('00'O)); /* send assignment complete over the new channel */ - RSL.send(ts_RSL_EST_IND(new_chan_nr, valueof(ts_RslLinkID_DCCH(0)), + rsl_pt.send(ts_RSL_EST_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_ass_cmpl_seen := true; /* obtain channel activation from RSL_Emulation for new channel */ - var RSL_Message chan_act := f_rslem_get_last_act(RSL_PROC, 0, g_chan_nr); + var RSL_Message chan_act := f_rslem_get_last_act(rsl_proc_pt, 0, g_chan_nr); /* check it (e.g. for correct ciphering parameters) */ f_check_chan_act(st, chan_act); repeat; @@ -878,20 +1255,20 @@ altstep as_assignment(inout AssignmentState st) runs on MSC_ConnHdlr { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected L3 received", l3)); } } - [st.rr_ass_cmpl_seen] RSL.receive(tr_RSL_DEACT_SACCH(st.old_chan_nr)) { + [st.rr_ass_cmpl_seen] rsl_pt.receive(tr_RSL_DEACT_SACCH(st.old_chan_nr)) { st.old_lchan_deact_sacch_seen := true; repeat; } - [st.rr_ass_cmpl_seen] RSL.receive(tr_RSL_REL_REQ(st.old_chan_nr, tr_RslLinkID_DCCH(0))) { + [st.rr_ass_cmpl_seen] rsl_pt.receive(tr_RSL_REL_REQ(st.old_chan_nr, tr_RslLinkID_DCCH(0))) { st.old_lchan_rll_rel_req_seen := true; - RSL.send(ts_RSL_REL_CONF(st.old_chan_nr, valueof(ts_RslLinkID_DCCH(0)))); + rsl_pt.send(ts_RSL_REL_CONF(st.old_chan_nr, valueof(ts_RslLinkID_DCCH(0)))); repeat; } - [st.rr_ass_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)); + [st.rr_ass_cmpl_seen] rsl_pt.receive(tr_RSL_RF_CHAN_REL(st.old_chan_nr)) { + rsl_pt.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); + f_rslem_unregister(0, st.old_chan_nr, PT := rsl_proc_pt); st.assignment_done := true; repeat; } @@ -901,17 +1278,19 @@ altstep as_modify(inout AssignmentState st) runs on MSC_ConnHdlr { /* no assignment, just mode modify */ var RSL_Message rsl; - [st.voice_call and not st.rr_modify_seen] RSL.receive(tr_RSL_DATA_REQ(g_chan_nr)) -> value rsl { + [st.rtp_stream and not st.rr_modify_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.channelModeModify)) { + st.rr_channel_mode_modify_msg := l3; f_rsl_reply(ts_RRM_ModeModifyAck(l3.msgs.rrm.channelModeModify.channelDescription, l3.msgs.rrm.channelModeModify.channelMode), rsl); st.rr_modify_seen := true; } repeat; } - [st.voice_call and st.rr_modify_seen] RSL.receive(tr_RSL_MsgTypeD(RSL_MT_MODE_MODIFY_REQ)) -> value rsl { + [st.rtp_stream and st.rr_modify_seen] RSL.receive(tr_RSL_MsgTypeD(RSL_MT_MODE_MODIFY_REQ)) -> value rsl { + st.rsl_mode_modify_msg := rsl; RSL.send(ts_RSL_MODE_MODIFY_ACK(g_chan_nr)); st.modify_done := true; repeat; @@ -953,21 +1332,30 @@ return boolean { /* Determine if the channel mode specified within rsl_chan_nr requires a * MODE MODIFY in to match the channel mode specified by given BSSMAP * ChannelType */ -function f_channel_needs_modify(BSSMAP_IE_ChannelType bssmap, RslChannelNr rsl_chan_nr) +function f_channel_needs_modify(TELNETasp_PT vty, BSSMAP_IE_ChannelType bssmap, RslChannelNr rsl_chan_nr) return boolean { - /* FIXME: This tests the rsl_chan_nr to determine if we are on a - * signalling channel or not. Unfortunately this may lead to false - * results if we are on a TCH. The problem is that a TCH may be also - * used in signalling mode, but this function assumes that only SDCCH4 - * and SDCCH8 are used as signalling channels at all. */ - var boolean current_signalling := false; var boolean desired_signalling := false; select (rsl_chan_nr) { case (t_RslChanNr_SDCCH4(?, ?)) { current_signalling := true; } case (t_RslChanNr_SDCCH8(?, ?)) { current_signalling := true; } + case (t_RslChanNr_Bm(?)) { + /* TCH/F, always subslot 0 */ + var charstring res := f_vty_transceive_ret(vty, "show lchan 0 0 " & int2str(rsl_chan_nr.tn) & " 0"); + if (f_strstr(res, "Channel Mode / Codec: SIGNALLING", 0) >= 0) { + current_signalling := true; + } + } + case (t_RslChanNr_Lm(?, ?)) { + /* TCH/H */ + var charstring res := f_vty_transceive_ret(vty, "show lchan 0 0 " & int2str(rsl_chan_nr.tn) + & " " & int2str(rsl_chan_nr.u.lm.sub_chan)); + if (f_strstr(res, "Channel Mode / Codec: SIGNALLING", 0) >= 0) { + current_signalling := true; + } + } } if (bssmap.speechOrDataIndicator == '0011'B) { @@ -1072,29 +1460,10 @@ runs on MSC_ConnHdlr { f_ass_patch_lcls(ass_tpl, exp_ass_cpl); f_create_chan_and_exp(); - /* we should now have a COMPL_L3 at the MSC */ - - var template PDU_BSSAP exp_l3_compl; - exp_l3_compl := tr_BSSMAP_ComplL3() - if (g_pars.aoip == false) { - exp_l3_compl.pdu.bssmap.completeLayer3Information.codecList := omit; - } else { - exp_l3_compl.pdu.bssmap.completeLayer3Information.codecList := ?; - } - T.start; - alt { - [] BSSAP.receive(exp_l3_compl); - [] BSSAP.receive(tr_BSSMAP_ComplL3) { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received non-matching COMPLETE LAYER 3 INFORMATION"); - } - [] T.timeout { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for COMPLETE LAYER 3 INFORMATION"); - } - } /* start ciphering, if requested */ if (ispresent(g_pars.encr)) { - f_cipher_mode(g_pars.encr.enc_alg, g_pars.encr.enc_key); + f_cipher_mode(g_pars.encr); } /* bail out early if no assignment requested */ @@ -1114,9 +1483,9 @@ runs on MSC_ConnHdlr { transid := omit }; var AssignmentState st := valueof(ts_AssignmentStateInit); - /* if the channel type is SIGNAL, we're not handling a voice call */ + /* if the channel type is SIGNAL, we're not handling an rtp stream */ if (ass_cmd.pdu.bssmap.assignmentRequest.channelType.speechOrDataIndicator != '0011'B) { - st.voice_call := true; + st.rtp_stream := true; exp_modify := true; } @@ -1132,7 +1501,8 @@ runs on MSC_ConnHdlr { * channel, we must now check if the mode of the current * channel is compatible. If not we expect the BSC to modify * the mode */ - exp_modify := f_channel_needs_modify(ass_cmd.pdu.bssmap.assignmentRequest.channelType, g_chan_nr); + st.is_assignment := false; + exp_modify := f_channel_needs_modify(BSCVTY, ass_cmd.pdu.bssmap.assignmentRequest.channelType, g_chan_nr); } /* Some test situations will involve MGCP transactions on a media @@ -1145,8 +1515,8 @@ runs on MSC_ConnHdlr { g_media.mgcp_conn[0].mdcx_seen_exp := 0; g_media.mgcp_conn[1].crcx_seen_exp := 0; g_media.mgcp_conn[1].mdcx_seen_exp := 0; - } else if (st.voice_call) { - /* For voice calls we expect the following MGCP activity */ + } else if (st.rtp_stream) { + /* For RTP streams we expect the following MGCP activity */ g_media.mgcp_conn[0].crcx_seen_exp := 1; g_media.mgcp_conn[0].mdcx_seen_exp := 1; g_media.mgcp_conn[1].crcx_seen_exp := 1; @@ -1173,8 +1543,8 @@ runs on MSC_ConnHdlr { /* modify related bits */ [not st.is_assignment and exp_modify] as_modify(st); - /* voice call related bits (IPA CRCX/MDCX + MGCP) */ - [st.voice_call] as_Media(); + /* RTP stream related bits (IPA CRCX/MDCX + MGCP) */ + [st.rtp_stream] as_Media(); /* if we receive exactly what we expected, always return + pass */ [st.is_assignment and st.assignment_done or (not st.is_assignment and (st.modify_done or not exp_modify))] BSSAP.receive(exp_ass_cpl) -> value bssap { @@ -1206,6 +1576,65 @@ runs on MSC_ConnHdlr { mtc.stop; } + if (exp_modify) { + /* Verify that the RR Channel Mode Modify and RSL MODE MODIFY message asked for the expected channel + * mode. */ + /* TODO: more precisely expect the different types of speech? */ + var OCT1 rr_channel_mode := st.rr_channel_mode_modify_msg.msgs.rrm.channelModeModify.channelMode.mode; + + if (st.rtp_stream and rr_channel_mode == '00'O) { + setverdict(fail, "f_establish_fully(): Expected RR Channel Mode Modify", + " to a speech mode, but got channelMode == ", rr_channel_mode); + mtc.stop; + } else if (not st.rtp_stream and rr_channel_mode != '00'O) { + setverdict(fail, "f_establish_fully(): Expected RR Channel Mode Modify", + " to signalling mode, but got channelMode == ", rr_channel_mode); + mtc.stop; + } + + var RSL_IE_Body chan_mode_ie; + if (not f_rsl_find_ie(st.rsl_mode_modify_msg, RSL_IE_CHAN_MODE, chan_mode_ie)) { + setverdict(fail, "RSL MODE MODIFY message lacks a Channel Mode IE"); + mtc.stop; + } + var RSL_SpeechDataInd rsl_spd_ind := chan_mode_ie.chan_mode.spd_ind; + if (st.rtp_stream and rsl_spd_ind != RSL_SPDI_SPEECH) { + setverdict(fail, "f_establish_fully(): Expected RSL MODE MODIFY", + " to a speech mode, but got spd_ind == ", rsl_spd_ind); + mtc.stop; + } else if (not st.rtp_stream and rsl_spd_ind != RSL_SPDI_SIGN) { + setverdict(fail, "f_establish_fully(): Expected RSL MODE MODIFY", + " to signalling mode, but got spd_ind == ", rsl_spd_ind); + mtc.stop; + } + + if (not istemplatekind(g_pars.expect_tsc, "omit")) { + var uint3_t got_tsc := rr_chan_desc_tsc(st.rr_channel_mode_modify_msg.msgs.rrm.channelModeModify.channelDescription); + if (not match(got_tsc, g_pars.expect_tsc)) { + setverdict(fail, "RR Channel Mode Modify: unexpected TSC in Channel Description: expected ", + g_pars.expect_tsc, " got ", got_tsc); + mtc.stop; + } + } + + } else { + /* not exp_modify, so this did a Channel Activate */ + + /* Check the TSC */ + if (not istemplatekind(g_pars.expect_tsc, "omit")) { + var RSL_Message chan_act := f_rslem_get_last_act(RSL_PROC, 0, g_chan_nr); + var RSL_IE_Body ie; + if (f_rsl_find_ie(chan_act, RSL_IE_CHAN_IDENT, ie)) { + var uint3_t got_tsc := ie.chan_ident.ch_desc.v.tsc; + if (not match(got_tsc, g_pars.expect_tsc)) { + setverdict(fail, "RSL CHANnel ACTIVation: unexpected TSC in Channel Description: expected ", + g_pars.expect_tsc, " got ", got_tsc); + mtc.stop; + } + } + } + } + /* When the BSC detects that LCLS is possible it will cross the * connetions that point to the PBX side of the MGW. In our case this * is mgcp_conn[1]. The BSC performs this operation already before the @@ -1218,17 +1647,17 @@ runs on MSC_ConnHdlr { } } - if (not exp_fail and st.voice_call and not g_pars.aoip) { + if (not exp_fail and st.rtp_stream and not g_pars.aoip) { /* With SCCPLite, connect to BSC-located MGW using a CRCX + SDP. It is sent in MGCP over IPA in the BSC<->MSC (BSC-NAT) connection. BSC will forward it to its MGW. */ var template MgcpCommand cmd; var template MgcpResponse resp; var integer cic := f_bssmap_ie_cic_2_int(ass_cmd.pdu.bssmap.assignmentRequest.circuitIdentityCode); - var MgcpEndpoint ep := int2str(cic) & "@mgw"; /* 1: matches value configured in BSC_Tests.ttcn pass in AssignReq */ + var MgcpEndpoint ep := int2str(cic) & "@mgw"; /* matches value configured in BSC_Tests.ttcn pass in AssignReq */ var MgcpCallId call_id := '51234'H; var SDP_attribute_list attributes := { valueof(ts_SDP_ptime(20)) }; - if (g_pars.use_osmux) { + if (g_pars.use_osmux_cn) { cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "sendrecv", call_id, cic); resp := tr_CRCX_ACK_osmux; } else { @@ -1253,6 +1682,13 @@ runs on MSC_ConnHdlr { " released properly: saw an RLL Release on the old lchan, but expecting none."); } } + + var charstring lchan_info := f_vty_transceive_ret(BSCVTY, "show lchan 0 0 " & int2str(g_chan_nr.tn) & " 0"); + if (f_strstr(lchan_info, "State: ESTABLISHED") < 0) { + log("after f_establish_fully(), 'show lchan' replied: ", lchan_info); + setverdict(fail, "lchan is not in state ESTABLISHED"); + mtc.stop; + } } type record HandoverState { @@ -1260,7 +1696,8 @@ type record HandoverState { boolean rr_ho_cmpl_seen, integer mdcx_seen_before_ho, boolean handover_done, - RslChannelNr old_chan_nr + RslChannelNr old_chan_nr, + uint3_t expect_target_tsc optional }; altstep as_handover(inout HandoverState st) runs on MSC_ConnHdlr { @@ -1275,6 +1712,19 @@ altstep as_handover(inout HandoverState st) runs on MSC_ConnHdlr { new_chan_nr, arfcn); /* FIXME: Determine TRX NR by ARFCN, instead of hard-coded TRX0! */ + /* Verify correct TSC in handoverCommand */ + if (ispresent(st.expect_target_tsc)) { + var uint3_t got_tsc := rr_chan_desc_tsc(l3.msgs.rrm.handoverCommand.channelDescription2); + if (not match(got_tsc, st.expect_target_tsc)) { + setverdict(fail, "RR Handover Command: unexpected TSC in Channel Description: expected ", + st.expect_target_tsc, " got ", got_tsc); + mtc.stop; + } else { + log("handoverCommand: verified TSC = ", got_tsc, " (matches ", + st.expect_target_tsc, ")"); + } + } + /* register our component for this channel number at the RSL Emulation */ f_rslem_register(0, new_chan_nr, RSL1_PROC); |