|
|
|
module GBProxy_Tests {
|
|
|
|
|
|
|
|
/* Osmocom GBProxy test suite in TTCN-3
|
|
|
|
* (C) 2020 sysmocom - s.f.m.c. GmbH
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Author: Daniel Willmann <dwillmann@sysmocom.de>
|
|
|
|
|
|
|
|
* Released under the terms of GNU General Public License, Version 2 or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
import from General_Types all;
|
|
|
|
import from Osmocom_Types all;
|
|
|
|
import from Misc_Helpers all;
|
|
|
|
import from GSM_Types all;
|
|
|
|
import from Native_Functions all;
|
|
|
|
import from NS_Types all;
|
|
|
|
import from NS_Emulation all;
|
|
|
|
import from BSSGP_Types all;
|
|
|
|
import from BSSGP_Emulation all;
|
|
|
|
import from SCCPasp_Types all;
|
|
|
|
import from Osmocom_Gb_Types all;
|
|
|
|
|
|
|
|
import from MobileL3_CommonIE_Types all;
|
|
|
|
import from MobileL3_GMM_SM_Types all;
|
|
|
|
import from MobileL3_Types all;
|
|
|
|
import from L3_Templates all;
|
|
|
|
import from L3_Common all;
|
|
|
|
|
|
|
|
import from TELNETasp_PortType all;
|
|
|
|
import from Osmocom_VTY_Functions all;
|
|
|
|
|
|
|
|
import from LLC_Types all;
|
|
|
|
import from LLC_Templates all;
|
|
|
|
|
|
|
|
import from GSM_RR_Types all;
|
|
|
|
|
|
|
|
/* mcc_mnc is 24.008 10.5.5.15 encoded. 262 42 */
|
|
|
|
const BcdMccMnc c_mcc_mnc := '262F42'H;
|
|
|
|
|
|
|
|
/* 48.016 section 6.1.4.2: The default maximum information field size of 1600 octets shall be supported on the Gb interface */
|
|
|
|
const integer max_fr_info_size := 1600;
|
|
|
|
|
|
|
|
modulepar {
|
|
|
|
boolean mp_enable_bss_load_sharing := false;
|
|
|
|
/* SGSN NS configuration */
|
|
|
|
NSConfigurations mp_nsconfig_sgsn := {
|
|
|
|
{
|
|
|
|
nsei := 101,
|
|
|
|
role_sgsn := true,
|
|
|
|
handle_sns := false,
|
|
|
|
nsvc := {
|
|
|
|
{
|
|
|
|
provider := {
|
|
|
|
ip := {
|
|
|
|
address_family := AF_INET,
|
|
|
|
local_udp_port := 7777,
|
|
|
|
local_ip := "127.0.0.1",
|
|
|
|
remote_udp_port := 23000,
|
|
|
|
remote_ip := "127.0.0.1"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
nsvci := 101
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/* BSS NSEI start at 2000 + x
|
|
|
|
* NSVCI start from value of NSEI + 100
|
|
|
|
* UDP port is NSVCI * 10 */
|
|
|
|
NSConfigurations mp_nsconfig_pcu := {
|
|
|
|
{
|
|
|
|
nsei := 2001,
|
|
|
|
role_sgsn := false,
|
|
|
|
handle_sns := false,
|
|
|
|
nsvc := {
|
|
|
|
{
|
|
|
|
provider := {
|
|
|
|
ip := {
|
|
|
|
address_family := AF_INET,
|
|
|
|
local_udp_port := 21010,
|
|
|
|
local_ip := "127.0.0.1",
|
|
|
|
remote_udp_port := 23000,
|
|
|
|
remote_ip := "127.0.0.1"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
nsvci := 2101
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
nsei := 2002,
|
|
|
|
role_sgsn := false,
|
|
|
|
handle_sns := false,
|
|
|
|
nsvc := {
|
|
|
|
{
|
|
|
|
provider := {
|
|
|
|
ip := {
|
|
|
|
address_family := AF_INET,
|
|
|
|
local_udp_port := 21020,
|
|
|
|
local_ip := "127.0.0.1",
|
|
|
|
remote_udp_port := 23000,
|
|
|
|
remote_ip := "127.0.0.1"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
nsvci := 2102
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
nsei := 2003,
|
|
|
|
role_sgsn := false,
|
|
|
|
handle_sns := false,
|
|
|
|
nsvc := {
|
|
|
|
{
|
|
|
|
provider := {
|
|
|
|
ip := {
|
|
|
|
address_family := AF_INET,
|
|
|
|
local_udp_port := 21030,
|
|
|
|
local_ip := "127.0.0.1",
|
|
|
|
remote_udp_port := 23000,
|
|
|
|
remote_ip := "127.0.0.1"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
nsvci := 2103
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/* BVCI are NSEI*10 + x
|
|
|
|
* The first NSE only has one BVC, the second one 2 and so on
|
|
|
|
* The Cell ID is BVCI + 10000
|
|
|
|
* LAC/RAC are configured in such a way that:
|
|
|
|
* LAC 13135 is present once in NSE(2001), twice in NSE(2002) and once in NSE(2003)
|
|
|
|
* LAC 13300 is present twice in NSE(2003)
|
|
|
|
* RAI 13135-1 is present in NSE(2002) and NSE(2003)
|
|
|
|
* RAI 13300-0 is present twice in NSE(2003)
|
|
|
|
*/
|
|
|
|
BssgpConfigs mp_gbconfigs := {
|
|
|
|
{
|
|
|
|
nsei := 2001,
|
|
|
|
sgsn_role := false,
|
|
|
|
bvc := {
|
|
|
|
{
|
|
|
|
bvci := 20011,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13135
|
|
|
|
},
|
|
|
|
rac := 0
|
|
|
|
},
|
|
|
|
cell_id := 30011
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
nsei := 2002,
|
|
|
|
sgsn_role := false,
|
|
|
|
bvc := {
|
|
|
|
{
|
|
|
|
bvci := 20021,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13135
|
|
|
|
},
|
|
|
|
rac := 1
|
|
|
|
},
|
|
|
|
cell_id := 30021
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
bvci := 20022,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13135
|
|
|
|
},
|
|
|
|
rac := 2
|
|
|
|
},
|
|
|
|
cell_id := 30022
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
nsei := 2003,
|
|
|
|
sgsn_role := false,
|
|
|
|
bvc := {
|
|
|
|
{
|
|
|
|
bvci := 20031,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13135
|
|
|
|
},
|
|
|
|
rac := 1
|
|
|
|
},
|
|
|
|
cell_id := 30031
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
bvci := 20032,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13300
|
|
|
|
},
|
|
|
|
rac := 0
|
|
|
|
},
|
|
|
|
cell_id := 30032
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
bvci := 20033,
|
|
|
|
cell_id := {
|
|
|
|
ra_id := {
|
|
|
|
lai := {
|
|
|
|
mcc_mnc := c_mcc_mnc,
|
|
|
|
lac := 13300
|
|
|
|
},
|
|
|
|
rac := 0
|
|
|
|
},
|
|
|
|
cell_id := 30033
|
|
|
|
},
|
|
|
|
depth := BSSGP_DECODE_DEPTH_BSSGP,
|
|
|
|
create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
type record GbInstance {
|
|
|
|
NS_CT vc_NS,
|
|
|
|
BSSGP_CT vc_BSSGP,
|
|
|
|
BSSGP_BVC_CTs vc_BSSGP_BVC,
|
|
|
|
BssgpConfig cfg
|
|
|
|
};
|
|
|
|
type record of BSSGP_BVC_CT BSSGP_BVC_CTs
|
|
|
|
|
|
|
|
const integer NUM_PCU := 3;
|
|
|
|
type record of GbInstance GbInstances;
|
|
|
|
type record of BssgpConfig BssgpConfigs;
|
|
|
|
type record of NSConfiguration NSConfigurations;
|
|
|
|
type record of BssgpCellId BssgpCellIds;
|
|
|
|
|
|
|
|
const integer NUM_SGSN := 1;
|
|
|
|
|
|
|
|
type component test_CT {
|
|
|
|
var GbInstances g_pcu;
|
|
|
|
var GbInstances g_sgsn;
|
|
|
|
|
|
|
|
port BSSGP_CT_PROC_PT PROC;
|
|
|
|
|
|
|
|
port BSSGP_BVC_MGMT_PT SGSN_MGMT;
|
|
|
|
port BSSGP_BVC_MGMT_PT PCU_MGMT;
|
|
|
|
|
|
|
|
port TELNETasp_PT GBPVTY;
|
|
|
|
|
|
|
|
var boolean g_initialized := false;
|
|
|
|
var boolean g_use_echo := false;
|
|
|
|
|
|
|
|
var ro_integer g_roi := {};
|
|
|
|
timer g_Tguard;
|
|
|
|
};
|
|
|
|
|
|
|
|
type component BSSGP_ConnHdlr {
|
|
|
|
/* array of per-BVC ports on the PCU side */
|
|
|
|
port BSSGP_PT PCU_PTP[NUM_PCU];
|
|
|
|
port BSSGP_PT PCU_SIG[NUM_PCU];
|
|
|
|
port BSSGP_PROC_PT PCU_PROC[NUM_PCU];
|
|
|
|
/* component reference to the component to which we're currently connected */
|
|
|
|
var BSSGP_BVC_CT pcu_ct[NUM_PCU];
|
|
|
|
/* BSSGP BVC configuration of the component to which we're currently connected */
|
|
|
|
var BssgpBvcConfig pcu_bvc_cfg[NUM_PCU];
|
|
|
|
|
|
|
|
/* array of per-BVC ports on the SGSN side */
|
|
|
|
port BSSGP_PT SGSN_PTP[NUM_SGSN];
|
|
|
|
port BSSGP_PT SGSN_SIG[NUM_SGSN];
|
|
|
|
port BSSGP_PROC_PT SGSN_PROC[NUM_SGSN];
|
|
|
|
/* component reference to the component to which we're currently connected */
|
|
|
|
var BSSGP_BVC_CT sgsn_ct[NUM_PCU];
|
|
|
|
|
|
|
|
var BSSGP_ConnHdlrPars g_pars;
|
|
|
|
timer g_Tguard;
|
|
|
|
var LLC_Entities llc;
|
|
|
|
|
|
|
|
var ro_integer g_roi := {};
|
|
|
|
}
|
|
|
|
|
|
|
|
type record SGSN_ConnHdlrNetworkPars {
|
|
|
|
boolean expect_ptmsi,
|
|
|
|
boolean expect_auth,
|
|
|
|
boolean expect_ciph
|
|
|
|
};
|
|
|
|
|
|
|
|
type record BSSGP_ConnHdlrPars {
|
|
|
|
/* IMEI of the simulated ME */
|
|
|
|
hexstring imei,
|
|
|
|
/* IMSI of the simulated MS */
|
|
|
|
hexstring imsi,
|
|
|
|
/* MSISDN of the simulated MS (probably unused) */
|
|
|
|
hexstring msisdn,
|
|
|
|
/* P-TMSI allocated to the simulated MS */
|
|
|
|
OCT4 p_tmsi optional,
|
|
|
|
OCT3 p_tmsi_sig optional,
|
|
|
|
/* TLLI of the simulated MS */
|
|
|
|
OCT4 tlli,
|
|
|
|
OCT4 tlli_old optional,
|
|
|
|
RoutingAreaIdentificationV ra optional,
|
|
|
|
GbInstances pcu,
|
|
|
|
GbInstances sgsn,
|
|
|
|
float t_guard
|
|
|
|
};
|
|
|
|
|
|
|
|
private function f_cellid_to_RAI(in BssgpCellId cell_id) return RoutingAreaIdentificationV {
|
|
|
|
/* mcc_mnc is encoded as of 24.008 10.5.5.15 */
|
|
|
|
var BcdMccMnc mcc_mnc := cell_id.ra_id.lai.mcc_mnc;
|
|
|
|
|
|
|
|
var RoutingAreaIdentificationV ret := {
|
|
|
|
mccDigit1 := mcc_mnc[0],
|
|
|
|
mccDigit2 := mcc_mnc[1],
|
|
|
|
mccDigit3 := mcc_mnc[2],
|
|
|
|
mncDigit3 := mcc_mnc[3],
|
|
|
|
mncDigit1 := mcc_mnc[4],
|
|
|
|
mncDigit2 := mcc_mnc[5],
|
|
|
|
lac := int2oct(cell_id.ra_id.lai.lac, 16),
|
|
|
|
rac := int2oct(cell_id.ra_id.rac, 8)
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
private function f_fix_create_cb(inout BssgpConfig cfg)
|
|
|
|
{
|
|
|
|
for (var integer i := 0; i < lengthof(cfg.bvc); i := i + 1) {
|
|
|
|
if (not isbound(cfg.bvc[i].create_cb)) {
|
|
|
|
cfg.bvc[i].create_cb := refers(BSSGP_Emulation.DefaultCreateCallback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_init_gb_pcu(inout GbInstance gb, charstring id, integer offset) runs on test_CT {
|
|
|
|
var charstring ns_id := id & "-NS(PCU[" & int2str(offset) & "])";
|
|
|
|
var charstring bssgp_id := id & "-BSSGP(PCU[" & int2str(offset) & "])";
|
|
|
|
gb.vc_NS := NS_CT.create(ns_id);
|
|
|
|
gb.vc_BSSGP := BSSGP_CT.create(bssgp_id);
|
|
|
|
/* connect lower end of BSSGP emulation with NS upper port */
|
|
|
|
connect(gb.vc_BSSGP:BSCP, gb.vc_NS:NS_SP);
|
|
|
|
|
|
|
|
gb.vc_NS.start(NSStart(mp_nsconfig_pcu[offset], ns_id));
|
|
|
|
gb.vc_BSSGP.start(BssgpStart(gb.cfg, bssgp_id));
|
|
|
|
|
|
|
|
for (var integer i := 0; i < lengthof(gb.cfg.bvc); i := i + 1) {
|
|
|
|
connect(self:PROC, gb.vc_BSSGP:PROC);
|
|
|
|
gb.vc_BSSGP_BVC[i] := f_bssgp_get_bvci_ct(gb.cfg.bvc[i].bvci, PROC);
|
|
|
|
disconnect(self:PROC, gb.vc_BSSGP:PROC);
|
|
|
|
connect(self:PCU_MGMT, gb.vc_BSSGP_BVC[i]:MGMT);
|
|
|
|
}
|
|
|
|
connect(self:PCU_MGMT, gb.vc_BSSGP:MGMT);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_init_gb_sgsn(inout GbInstance gb, charstring id, integer offset) runs on test_CT {
|
|
|
|
var charstring ns_id := id & "-NS(SGSN[" & int2str(offset) & "])";
|
|
|
|
var charstring bssgp_id := id & "-BSSGP(SGSN[" & int2str(offset) & "])";
|
|
|
|
gb.vc_NS := NS_CT.create(ns_id);
|
|
|
|
gb.vc_BSSGP := BSSGP_CT.create(bssgp_id);
|
|
|
|
/* connect lower end of BSSGP emulation with NS upper port */
|
|
|
|
connect(gb.vc_BSSGP:BSCP, gb.vc_NS:NS_SP);
|
|
|
|
|
|
|
|
gb.vc_NS.start(NSStart(mp_nsconfig_sgsn[offset], ns_id));
|
|
|
|
gb.vc_BSSGP.start(BssgpStart(gb.cfg, bssgp_id));
|
|
|
|
|
|
|
|
for (var integer i := 0; i < lengthof(gb.cfg.bvc); i := i + 1) {
|
|
|
|
connect(self:PROC, gb.vc_BSSGP:PROC);
|
|
|
|
gb.vc_BSSGP_BVC[i] := f_bssgp_get_bvci_ct(gb.cfg.bvc[i].bvci, PROC);
|
|
|
|
disconnect(self:PROC, gb.vc_BSSGP:PROC);
|
|
|
|
connect(self:SGSN_MGMT, gb.vc_BSSGP_BVC[i]:MGMT);
|
|
|
|
}
|
|
|
|
connect(self:SGSN_MGMT, gb.vc_BSSGP:MGMT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function f_destroy_gb(inout GbInstance gb) runs on test_CT {
|
|
|
|
gb.vc_NS.stop;
|
|
|
|
gb.vc_BSSGP.stop;
|
|
|
|
|
|
|
|
for (var integer i := 0; i < lengthof(gb.cfg.bvc); i := i + 1) {
|
|
|
|
gb.vc_BSSGP_BVC[i].stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_init_vty() runs on test_CT {
|
|
|
|
map(self:GBPVTY, system:GBPVTY);
|
|
|
|
f_vty_set_prompts(GBPVTY);
|
|
|
|
f_vty_transceive(GBPVTY, "enable");
|
|
|
|
}
|
|
|
|
|
|
|
|
type record of integer ro_integer;
|
|
|
|
|
|
|
|
private function ro_integer_contains(ro_integer r, integer x) return boolean {
|
|
|
|
for (var integer j := 0; j < lengthof(r); j := j+1) {
|
|
|
|
if (r[j] == x) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function f_init(float t_guard := 30.0) runs on test_CT {
|
|
|
|
var ro_integer bvci_unblocked := {};
|
|
|
|
var BssgpStatusIndication bsi;
|
|
|
|
var integer i;
|
|
|
|
|
|
|
|
if (g_initialized == true) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
g_initialized := true;
|
|
|
|
|
|
|
|
g_Tguard.start(t_guard);
|
|
|
|
activate(as_gTguard(g_Tguard));
|
|
|
|
|
|
|
|
g_sgsn[0].cfg := {
|
|
|
|
nsei := mp_nsconfig_sgsn[0].nsei,
|
|
|
|
sgsn_role := true,
|
|
|
|
bvc := { }
|
|
|
|
}
|
|
|
|
for (i := 0; i < lengthof(mp_gbconfigs); i := i+1) {
|
|
|
|
g_pcu[i].cfg := mp_gbconfigs[i];
|
|
|
|
/* make sure all have a proper crate_cb, which cannot be specified in config file */
|
|
|
|
f_fix_create_cb(g_pcu[i].cfg);
|
|
|
|
/* concatenate all the PCU-side BVCs for the SGSN side */
|
|
|
|
g_sgsn[0].cfg.bvc := g_sgsn[0].cfg.bvc & g_pcu[i].cfg.bvc;
|
|
|
|
}
|
|
|
|
|
|
|
|
f_init_vty();
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) {
|
|
|
|
f_vty_transceive(GBPVTY, "nsvc nsei " & int2str(g_sgsn[i].cfg.nsei) & " force-unconfigured");
|
|
|
|
}
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) {
|
|
|
|
f_vty_transceive(GBPVTY, "nsvc nsei " & int2str(g_pcu[i].cfg.nsei) & " force-unconfigured");
|
|
|
|
f_vty_transceive(GBPVTY, "delete-gbproxy-peer " & int2str(g_pcu[i].cfg.nsei) & " only-bvc");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) {
|
|
|
|
f_init_gb_sgsn(g_sgsn[i], "GbProxy_Test", i);
|
|
|
|
}
|
|
|
|
f_sleep(4.0);
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) {
|
|
|
|
f_init_gb_pcu(g_pcu[i], "GbProxy_Test", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* wait until all BVC are unblocked on both sides */
|
|
|
|
timer T := 15.0;
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[] SGSN_MGMT.receive(BssgpStatusIndication:{*, ?, BVC_S_UNBLOCKED}) -> value bsi {
|
|
|
|
bvci_unblocked := bvci_unblocked & { bsi.bvci };
|
|
|
|
if (lengthof(bvci_unblocked) != lengthof(g_sgsn[0].cfg.bvc)) {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[] SGSN_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
[] SGSN_MGMT.receive(BssgpResetIndication:?) {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
[] SGSN_MGMT.receive {
|
|
|
|
setverdict(fail, "Received unexpected message on SGSN_MGMT");
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
|
|
|
|
[] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, BVC_S_UNBLOCKED}) -> value bsi {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
[] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
[] PCU_MGMT.receive(BssgpResetIndication:{0}) {
|
|
|
|
repeat;
|
|
|
|
}
|
|
|
|
[] PCU_MGMT.receive {
|
|
|
|
setverdict(fail, "Received unexpected message on PCU_MGMT");
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "Timeout waiting for unblock of all BVCs");
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* iterate over list and check all BVCI */
|
|
|
|
for (i := 0; i < lengthof(g_sgsn[0].cfg.bvc); i := i+1) {
|
|
|
|
var BssgpBvci bvci := g_sgsn[0].cfg.bvc[i].bvci;
|
|
|
|
if (not ro_integer_contains(bvci_unblocked, bvci)) {
|
|
|
|
setverdict(fail, "BVCI=", bvci, " was not unblocked during start-up");
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* re-start guard timer after all BVCs are up, so it only counts the actual test case */
|
|
|
|
g_Tguard.start(t_guard);
|
|
|
|
}
|
|
|
|
|
|
|
|
function f_cleanup() runs on test_CT {
|
|
|
|
var integer i;
|
|
|
|
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) {
|
|
|
|
f_destroy_gb(g_sgsn[i]);
|
|
|
|
}
|
|
|
|
for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) {
|
|
|
|
f_destroy_gb(g_pcu[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
type function void_fn(charstring id) runs on BSSGP_ConnHdlr;
|
|
|
|
|
|
|
|
/* helper function to create, connect and start a BSSGP_ConnHdlr component */
|
|
|
|
function f_start_handler(void_fn fn, charstring id, GbInstances pcu, GbInstances sgsn, integer imsi_suffix,
|
|
|
|
float t_guard := 30.0)
|
|
|
|
runs on test_CT return BSSGP_ConnHdlr {
|
|
|
|
var BSSGP_ConnHdlr vc_conn;
|
|
|
|
|
|
|
|
var BSSGP_ConnHdlrPars pars := {
|
|
|
|
imei := f_gen_imei(imsi_suffix),
|
|
|
|
imsi := f_gen_imsi(imsi_suffix),
|
|
|
|
msisdn := f_gen_msisdn(imsi_suffix),
|
|
|
|
p_tmsi := omit,
|
|
|
|
p_tmsi_sig := omit,
|
|
|
|
tlli := f_gprs_tlli_random(),
|
|
|
|
tlli_old := omit,
|
|
|
|
ra := omit,
|
|
|
|
pcu := g_pcu,
|
|
|
|
sgsn := g_sgsn,
|
|
|
|
t_guard := t_guard
|
|
|
|
};
|
|
|
|
|
|
|
|
vc_conn := BSSGP_ConnHdlr.create(id);
|
|
|
|
|
|
|
|
vc_conn.start(f_handler_init(fn, id, pars));
|
|
|
|
return vc_conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Connect the PCU-side per-BVC ports (PCU/PCU_SIG/PCU_PROC) array slot 'port_idx' to specified per-BVC component */
|
|
|
|
private function f_connect_to_pcu_bvc(integer port_idx, integer nse_idx, integer bvc_idx)
|
|
|
|
runs on BSSGP_ConnHdlr {
|
|
|
|
var BSSGP_BVC_CT bvc_ct := g_pars.pcu[nse_idx].vc_BSSGP_BVC[bvc_idx]
|
|
|
|
if (PCU_PTP[port_idx].checkstate("Connected")) {
|
|
|
|
/* unregister + disconnect from old BVC */
|
|
|
|
f_client_unregister(g_pars.imsi, PCU_PROC[port_idx]);
|
|
|
|
disconnect(self:PCU_PTP[port_idx], pcu_ct[port_idx]:BSSGP_SP);
|
|
|
|
disconnect(self:PCU_SIG[port_idx], pcu_ct[port_idx]:BSSGP_SP_SIG);
|
|
|
|
disconnect(self:PCU_PROC[port_idx], pcu_ct[port_idx]:BSSGP_PROC);
|
|
|
|
}
|
|
|
|
/* connect to new BVC and register us */
|
|
|
|
connect(self:PCU_PTP[port_idx], bvc_ct:BSSGP_SP);
|
|
|
|
connect(self:PCU_SIG[port_idx], bvc_ct:BSSGP_SP_SIG);
|
|
|
|
connect(self:PCU_PROC[port_idx], bvc_ct:BSSGP_PROC);
|
|
|
|
f_client_register(g_pars.imsi, g_pars.tlli, PCU_PROC[port_idx]);
|
|
|
|
pcu_ct[port_idx] := bvc_ct;
|
|
|
|
pcu_bvc_cfg[port_idx] := g_pars.pcu[nse_idx].cfg.bvc[bvc_idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Connect the SGSN-side per-BVC ports (SGSN/SGSN_SIG/SGSN_PROC) array slot 'port_idx' to specified per-BVC component */
|
|
|
|
private function f_connect_to_sgsn_bvc(integer port_idx, BSSGP_BVC_CT bvc_ct) runs on BSSGP_ConnHdlr {
|
|
|
|
if (SGSN_PTP[port_idx].checkstate("Connected")) {
|
|
|
|
/* unregister + disconnect from old BVC */
|
|
|
|
f_client_unregister(g_pars.imsi, SGSN_PROC[port_idx]);
|
|
|
|
disconnect(self:SGSN_PTP[port_idx], sgsn_ct[port_idx]:BSSGP_SP);
|
|
|
|
disconnect(self:SGSN_SIG[port_idx], sgsn_ct[port_idx]:BSSGP_SP_SIG);
|
|
|
|
disconnect(self:SGSN_PROC[port_idx], sgsn_ct[port_idx]:BSSGP_PROC);
|
|
|
|
}
|
|
|
|
/* connect to new BVC and register us */
|
|
|
|
connect(self:SGSN_PTP[port_idx], bvc_ct:BSSGP_SP);
|
|
|
|
connect(self:SGSN_SIG[port_idx], bvc_ct:BSSGP_SP_SIG);
|
|
|
|
connect(self:SGSN_PROC[port_idx], bvc_ct:BSSGP_PROC);
|
|
|
|
f_client_register(g_pars.imsi, g_pars.tlli, SGSN_PROC[port_idx]);
|
|
|
|
sgsn_ct[port_idx] := bvc_ct;
|
|
|
|
}
|
|
|
|
|
|
|
|
private altstep as_gTguard(timer Tguard) {
|
|
|
|
[] Tguard.timeout {
|
|
|
|
setverdict(fail, "Tguard timeout");
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* first function called in every ConnHdlr */
|
|
|
|
private function f_handler_init(void_fn fn, charstring id, BSSGP_ConnHdlrPars pars)
|
|
|
|
runs on BSSGP_ConnHdlr {
|
|
|
|
var integer i;
|
|
|
|
/* do some common stuff like setting up g_pars */
|
|
|
|
g_pars := pars;
|
|
|
|
|
|
|
|
llc := f_llc_create(false);
|
|
|
|
|
|
|
|
/* default connections on PCU side: First BVC of each NSE/PCU */
|
|
|
|
for (i := 0; i < lengthof(g_pars.pcu); i := i+1) {
|
|
|
|
f_connect_to_pcu_bvc(port_idx := i, nse_idx := i, bvc_idx := 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* default connections on SGSN side: First BVC of each NSE/SGSN */
|
|
|
|
for (i := 0; i < lengthof(g_pars.sgsn); i := i+1) {
|
|
|
|
f_connect_to_sgsn_bvc(i, g_pars.sgsn[i].vc_BSSGP_BVC[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_Tguard.start(pars.t_guard);
|
|
|
|
activate(as_gTguard(g_Tguard));
|
|
|
|
|
|
|
|
/* call the user-supplied test case function */
|
|
|
|
fn.apply(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_client_register(hexstring imsi, OCT4 tlli, BSSGP_PROC_PT PT)
|
|
|
|
runs on BSSGP_ConnHdlr {
|
|
|
|
PT.call(BSSGP_register_client:{imsi, tlli}) {
|
|
|
|
[] PT.getreply(BSSGP_register_client:{imsi, tlli}) {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_client_unregister(hexstring imsi, BSSGP_PROC_PT PT)
|
|
|
|
runs on BSSGP_ConnHdlr {
|
|
|
|
PT.call(BSSGP_unregister_client:{imsi}) {
|
|
|
|
[] PT.getreply(BSSGP_unregister_client:{imsi}) {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send 'tx' on PTP-BVCI from PCU; expect 'rx' on SGSN */
|
|
|
|
friend function f_pcu2sgsn(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx,
|
|
|
|
integer pcu_idx := 0, integer sgsn_idx := 0, boolean use_sig := false) runs on BSSGP_ConnHdlr {
|
|
|
|
var PDU_BSSGP rx;
|
|
|
|
timer T := 1.0;
|
|
|
|
|
|
|
|
if (use_sig) {
|
|
|
|
PCU_SIG[pcu_idx].send(tx);
|
|
|
|
} else {
|
|
|
|
PCU_PTP[pcu_idx].send(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[use_sig] SGSN_SIG[sgsn_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[not use_sig] SGSN_PTP[sgsn_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[] SGSN_PTP[sgsn_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected BSSGP on SGSN side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] SGSN_SIG[sgsn_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected SIG BSSGP on SGSN side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "Timeout waiting for BSSGP on SGSN side: ", exp_rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send 'tx' on PTP-BVCI from SGSN; expect 'rx' on PCU */
|
|
|
|
friend function f_sgsn2pcu(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx,
|
|
|
|
integer sgsn_idx:= 0, integer pcu_idx := 0, boolean use_sig := false) runs on BSSGP_ConnHdlr {
|
|
|
|
var PDU_BSSGP rx;
|
|
|
|
timer T := 1.0;
|
|
|
|
|
|
|
|
if (use_sig) {
|
|
|
|
SGSN_SIG[sgsn_idx].send(tx);
|
|
|
|
} else {
|
|
|
|
SGSN_PTP[sgsn_idx].send(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[use_sig] PCU_SIG[pcu_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[not use_sig] PCU_PTP[pcu_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[] PCU_PTP[pcu_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected BSSGP on PCU side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] PCU_SIG[pcu_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected SIG BSSGP on PCU side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "Timeout waiting for BSSGP on PCU side: ", exp_rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* GlobaLTest_CT: Using the per-NSE GLOBAL ports on PCU + SGSN side
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
type component GlobalTest_CT extends test_CT {
|
|
|
|
port BSSGP_PT G_PCU[NUM_PCU];
|
|
|
|
port BSSGP_PT G_SGSN[NUM_SGSN];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* connect the signaling BVC of each NSE to the G_PCU / G_SGSN ports */
|
|
|
|
private function f_global_init() runs on GlobalTest_CT {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < lengthof(g_sgsn); i := i+1) {
|
|
|
|
connect(self:G_SGSN[i], g_sgsn[i].vc_BSSGP:GLOBAL);
|
|
|
|
}
|
|
|
|
for (i := 0; i < lengthof(g_pcu); i := i+1) {
|
|
|
|
connect(self:G_PCU[i], g_pcu[i].vc_BSSGP:GLOBAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* connect the first PTP BVC of each NSE to the G_PCU / G_SGSN ports */
|
|
|
|
private function f_global_init_ptp() runs on GlobalTest_CT {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < lengthof(g_sgsn); i := i+1) {
|
|
|
|
connect(self:G_SGSN[i], g_sgsn[i].vc_BSSGP_BVC[0]:GLOBAL);
|
|
|
|
}
|
|
|
|
for (i := 0; i < lengthof(g_pcu); i := i+1) {
|
|
|
|
connect(self:G_PCU[i], g_pcu[i].vc_BSSGP_BVC[0]:GLOBAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send 'tx' on PTP-BVCI from PCU; expect 'rx' on SGSN */
|
|
|
|
friend function f_global_pcu2sgsn(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx,
|
|
|
|
integer pcu_idx := 0, integer sgsn_idx := 0) runs on GlobalTest_CT {
|
|
|
|
var PDU_BSSGP rx;
|
|
|
|
timer T := 1.0;
|
|
|
|
|
|
|
|
G_PCU[pcu_idx].send(tx);
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[] G_SGSN[sgsn_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[] G_SGSN[sgsn_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected BSSGP on SGSN side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "Timeout waiting for BSSGP on SGSN side: ", exp_rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send 'tx' on PTP-BVCI from SGSN; expect 'rx' on PCU */
|
|
|
|
friend function f_global_sgsn2pcu(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx,
|
|
|
|
integer sgsn_idx := 0, integer pcu_idx := 0) runs on GlobalTest_CT {
|
|
|
|
var PDU_BSSGP rx;
|
|
|
|
timer T := 1.0;
|
|
|
|
|
|
|
|
G_SGSN[sgsn_idx].send(tx);
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[] G_PCU[pcu_idx].receive(exp_rx) {
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
[] G_PCU[pcu_idx].receive(PDU_BSSGP:?) -> value rx {
|
|
|
|
setverdict(fail, "Unexpected BSSGP on PCU side: ", rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "Timeout waiting for BSSGP on PCU side: ", exp_rx);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO:
|
|
|
|
* Detach without Attach
|
|
|
|
* SM procedures without attach / RAU
|
|
|
|
* ATTACH / RAU
|
|
|
|
** with / without authentication
|
|
|
|
** with / without P-TMSI allocation
|
|
|
|
* re-transmissions of LLC frames
|
|
|
|
* PDP Context activation
|
|
|
|
** with different GGSN config in SGSN VTY
|
|
|
|
** with different PDP context type (v4/v6/v46)
|
|
|
|
** timeout from GGSN
|
|
|
|
** multiple / secondary PDP context
|
|
|
|
*/
|
|
|
|
|
|
|
|
private function f_TC_BVC_bringup(charstring id) runs on BSSGP_ConnHdlr {
|
|
|
|
f_sleep(5.0);
|
|
|
|
setverdict(pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
testcase TC_BVC_bringup() runs on test_CT {
|
|
|
|
var BSSGP_ConnHdlr vc_conn;
|
|
|
|
f_init();
|
|
|
|
|
|
|
|
vc_conn := f_start_handler(refers(f_TC_BVC_bringup), testcasename(), g_pcu, g_sgsn, 51);
|
|
|
|
vc_conn.done;
|
|
|
|
|
|
|
|
f_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
friend function f_bssgp_suspend(integer ran_idx := 0) runs on BSSGP_ConnHdlr return OCT1 {
|
|
|
|
var BssgpBvcConfig bvcc := g_pars.pcu[ran_idx].cfg.bvc[0];
|
|
|
|
timer T := 5.0;
|
|
|
|
var PDU_BSSGP rx_pdu;
|
|
|
|
PCU_SIG[ran_idx].send(ts_BSSGP_SUSPEND(g_pars.tlli, bvcc.cell_id.ra_id));
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[] PCU_SIG[ran_idx].receive(tr_BSSGP_SUSPEND_ACK(g_pars.tlli, bvcc.cell_id.ra_id, ?)) -> value rx_pdu {
|
|
|
|
return rx_pdu.pDU_BSSGP_SUSPEND_ACK.suspend_Reference_Number.suspend_Reference_Number_value;
|
|
|
|
}
|
|
|
|
[] PCU_SIG[ran_idx].receive(tr_BSSGP_SUSPEND_NACK(g_pars.tlli, bvcc.cell_id.ra_id, ?)) -> value rx_pdu {
|
|
|
|
setverdict(fail, "SUSPEND-NACK in response to SUSPEND for TLLI ", g_pars.tlli);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "No SUSPEND-ACK in response to SUSPEND for TLLI ", g_pars.tlli);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return '00'O;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend function f_bssgp_resume(OCT1 susp_ref, integer ran_idx := 0) runs on BSSGP_ConnHdlr {
|
|
|
|
var BssgpBvcConfig bvcc := g_pars.pcu[ran_idx].cfg.bvc[0];
|
|
|
|
timer T := 5.0;
|
|
|
|
PCU_SIG[ran_idx].send(ts_BSSGP_RESUME(g_pars.tlli, bvcc.cell_id.ra_id, susp_ref));
|
|
|
|
T.start;
|
|
|
|
alt {
|
|
|
|
[] PCU_SIG[ran_idx].receive(tr_BSSGP_RESUME_ACK(g_pars.tlli, bvcc.cell_id.ra_id));
|
|
|
|
[] PCU_SIG[ran_idx].receive(tr_BSSGP_RESUME_NACK(g_pars.tlli, bvcc.cell_id.ra_id, ?)) {
|
|
|
|
setverdict(fail, "RESUME-NACK in response to RESUME for TLLI ", g_pars.tlli);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
[] T.timeout {
|
|
|
|
setverdict(fail, "No RESUME-ACK in response to SUSPEND for TLLI ", g_pars.tlli);
|
|
|
|
mtc.stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|