aboutsummaryrefslogtreecommitdiffstats
path: root/bsc/MSC_ConnectionHandler.ttcn
diff options
context:
space:
mode:
Diffstat (limited to 'bsc/MSC_ConnectionHandler.ttcn')
-rw-r--r--bsc/MSC_ConnectionHandler.ttcn768
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);