diff options
Diffstat (limited to 'gbproxy/GBProxy_Tests.ttcn')
-rw-r--r-- | gbproxy/GBProxy_Tests.ttcn | 3749 |
1 files changed, 3537 insertions, 212 deletions
diff --git a/gbproxy/GBProxy_Tests.ttcn b/gbproxy/GBProxy_Tests.ttcn index 10728b27..7a6572a3 100644 --- a/gbproxy/GBProxy_Tests.ttcn +++ b/gbproxy/GBProxy_Tests.ttcn @@ -1,6 +1,7 @@ module GBProxy_Tests { /* Osmocom GBProxy test suite in TTCN-3 + * (C) 2020-2021 Harald Welte <laforge@osmocom.org> * (C) 2020 sysmocom - s.f.m.c. GmbH * All rights reserved. * @@ -14,6 +15,7 @@ module GBProxy_Tests { 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; @@ -31,120 +33,347 @@ import from L3_Common all; import from TELNETasp_PortType all; import from Osmocom_VTY_Functions all; +import from Osmocom_CTRL_Adapter 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 { - /* IP/port on which we run our internal GSUP/HLR emulation */ - NSConfigurations_SGSN mp_nsconfig_sgsn := { + charstring mp_gbproxy_ip := "127.0.0.1"; + integer mp_gbproxy_ctrl_port := 4263; + /* NRI bit-length. 0 for no pooling */ + integer mp_nri_bitlength := 5; + roro_integer mp_sgsn_nri := { + { 3 }, /* list of NRIs of first SGSN */ + { 4 } /* list of NRIs of second SGSN */ + }; + boolean mp_enable_bss_load_sharing := false; + /* SGSN NS configuration */ + NSConfigurations mp_nsconfig_sgsn := { { - 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, nsei := 101, role_sgsn := true, - handle_sns := false + handle_sns := false, + nsvc := { + { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 7777, + local_ip := "127.0.0.10", + remote_udp_port := 23000, + remote_ip := "127.0.0.1", + data_weight := 0, + signalling_weight := 1 + } + }, + nsvci := 101 + }, { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 7770, + local_ip := "127.0.0.10", + remote_udp_port := 23000, + remote_ip := "127.0.0.1", + data_weight := 1, + signalling_weight := 0 + } + }, + nsvci := 201 + } + + } + }, { + nsei := 102, + role_sgsn := true, + handle_sns := false, + nsvc := { + { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 8888, + local_ip := "127.0.0.11", + remote_udp_port := 23000, + remote_ip := "127.0.0.1", + data_weight := 0, + signalling_weight := 1 + } + }, + nsvci := 102 + }, { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 8880, + local_ip := "127.0.0.11", + remote_udp_port := 23000, + remote_ip := "127.0.0.1", + data_weight := 1, + signalling_weight := 0 + } + }, + nsvci := 202 + } + } } }; - NSConfigurations_PCU mp_nsconfig_pcu := { + /* BSS NSEI start at 2000 + x + * NSVCI start from value of NSEI + 100 + * UDP port is NSVCI * 10 */ + NSConfigurations mp_nsconfig_pcu := { { - 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 := 97, - nsei := 96, + nsei := 2001, role_sgsn := false, - handle_sns := false + handle_sns := true, + nsvc := { + { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 21010, + local_ip := "127.0.1.1", + remote_udp_port := 23000, + remote_ip := "127.0.0.2", + data_weight := 1, + signalling_weight := 1 + } + }, + nsvci := 2101 + } + } }, { - provider := { - ip := { - address_family := AF_INET, - local_udp_port := 21011, - local_ip := "127.0.0.1", - remote_udp_port := 23000, - remote_ip := "127.0.0.1" - } - }, - nsvci := 98, - nsei := 97, + nsei := 2002, role_sgsn := false, - handle_sns := false + handle_sns := true, + nsvc := { + { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 21020, + local_ip := "127.0.2.1", + remote_udp_port := 23000, + remote_ip := "127.0.0.2", + data_weight := 1, + signalling_weight := 1 + } + }, + nsvci := 2102 + } + } }, { - provider := { - ip := { - address_family := AF_INET, - local_udp_port := 21012, - local_ip := "127.0.0.1", - remote_udp_port := 23000, - remote_ip := "127.0.0.1" - } - }, - nsvci := 99, - nsei := 98, + nsei := 2003, role_sgsn := false, - handle_sns := false + handle_sns := true, + nsvc := { + { + provider := { + ip := { + address_family := AF_INET, + local_udp_port := 21030, + local_ip := "127.0.3.1", + remote_udp_port := 23000, + remote_ip := "127.0.0.2", + data_weight := 1, + signalling_weight := 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) + } + } + } + } }; -const integer NUM_BVC_PER_NSE := 3; type record GbInstance { NS_CT vc_NS, BSSGP_CT vc_BSSGP, - BSSGP_BVC_CT vc_BSSGP_BVC[NUM_BVC_PER_NSE], + BSSGP_BVC_CTs vc_BSSGP_BVC, BssgpConfig cfg }; +type record of BSSGP_BVC_CT BSSGP_BVC_CTs const integer NUM_PCU := 3; -type record length(NUM_PCU) of GbInstance GbInstances_PCU; -type record length(NUM_PCU) of NSConfiguration NSConfigurations_PCU; -type record length(NUM_PCU) of BssgpCellId BssgpCellIds; +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 record length(NUM_SGSN) of GbInstance GbInstances_SGSN; -type record length(NUM_SGSN) of NSConfiguration NSConfigurations_SGSN; +/* you cannot simply change this to any larger number of SGSNs and expect all test + * cases to work. This would have overly complicated the code. Check below for + * tests that use interleave on SGSN_MGMT.receive() for each SGSN NSEI for example */ +const integer NUM_SGSN := 2; -type component test_CT { - var GbInstances_PCU g_pcu; - var GbInstances_SGSN g_sgsn; +type component test_CT extends CTRL_Adapter_CT { + var GbInstances g_pcu; + var GbInstances g_sgsn; + + port NS_CTRL_PT NS_CTRL; 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 := {}; + var roro_integer g_roroi := {}; + timer g_Tguard; }; type component BSSGP_ConnHdlr { - port BSSGP_PT PCU[NUM_PCU]; + /* 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]; - port BSSGP_PT SGSN[NUM_SGSN]; + /* 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 { @@ -167,10 +396,26 @@ type record BSSGP_ConnHdlrPars { OCT4 tlli, OCT4 tlli_old optional, RoutingAreaIdentificationV ra optional, - BssgpCellIds bssgp_cell_id, + GbInstances pcu, + GbInstances sgsn, + /* The SGSN index to be used within the test */ + integer sgsn_idx, float t_guard }; +private function get_bvc_idx_for_bvci(GbInstance gbi, BssgpBvci bvci) return integer +{ + var integer i; + + for (i := 0; i < lengthof(gbi.cfg.bvc); i := i + 1) { + if (gbi.cfg.bvc[i].bvci == bvci) { + return i; + } + } + setverdict(fail, "Could not find BVC Index for BVCI ", bvci); + return -1; +} + 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; @@ -188,221 +433,615 @@ private function f_cellid_to_RAI(in BssgpCellId cell_id) return RoutingAreaIdent 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 { - gb.vc_NS := NS_CT.create(id & "-NS(PCU)" & int2str(offset)); - gb.vc_BSSGP := BSSGP_CT.create(id & "-BSSGP(PCU)" & int2str(offset)); + 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) alive; + gb.vc_BSSGP := BSSGP_CT.create(bssgp_id) alive; /* 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])); - gb.vc_BSSGP.start(BssgpStart(gb.cfg, id)); + 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) { + /* obtain the component reference of the BSSGP_BVC_CT for each PTP BVC */ 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 all of the per-BVC MGMT ports to our PCU_MGMT port (1:N) */ + connect(self:PCU_MGMT, gb.vc_BSSGP_BVC[i]:MGMT); } + /* connect all of the BSSGP/NSE global MGMT port to our PCU_MGMT port (1:N) */ + 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 { - gb.vc_NS := NS_CT.create(id & "-NS(SGSN)" & int2str(offset)); - gb.vc_BSSGP := BSSGP_CT.create(id & "-BSSGP(SGSN)" & int2str(offset)); + 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) alive; + gb.vc_BSSGP := BSSGP_CT.create(bssgp_id) alive; /* 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])); - gb.vc_BSSGP.start(BssgpStart(gb.cfg, id)); + 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) { + /* obtain the component reference of the BSSGP_BVC_CT for each PTP BVC */ 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 all of the per-BVC MGMT ports to our SGSN_MGMT port (1:N) */ + connect(self:SGSN_MGMT, gb.vc_BSSGP_BVC[i]:MGMT); } + /* connect all of the BSSGP/NSE global MGMT port to our SGSN_MGMT port (1:N) */ + 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"); } -/* mcc_mnc is 24.008 10.5.5.15 encoded. 262 42 */ -function f_init(BcdMccMnc mcc_mnc := '262F42'H) runs on test_CT { +/* count the number of unblocked BVCI for each SGSN NSE */ +private altstep as_count_unblocked4nse(integer sgsn_idx, inout roro_integer bvci_unblocked) +runs on test_CT { + var BssgpStatusIndication bsi; + [] SGSN_MGMT.receive(BssgpStatusIndication:{g_sgsn[sgsn_idx].cfg.nsei, ?, BVC_S_UNBLOCKED}) -> value bsi { + bvci_unblocked[sgsn_idx] := bvci_unblocked[sgsn_idx] & { bsi.bvci }; + /* 'repeat' until sufficient number of BVC-rest has been received on all SGSNs */ + for (var integer i := 0; i < lengthof(bvci_unblocked); i := i+1) { + if (lengthof(bvci_unblocked[i]) < lengthof(g_sgsn[i].cfg.bvc)) { + repeat; + } + } + } +} + +private template (value) charstring ts_pcu_bvc_fsm_id(uint16_t nsei, uint16_t bvci) := + "NSE" & f_int2str(nsei, 5) & "-BVC" & f_int2str(bvci, 5); + +function f_bvc_fsm_ensure_state(uint16_t nsei, uint16_t bvci, template (present) charstring exp) +runs on CTRL_Adapter_CT { + f_ctrl_get_exp_inst_state(IPA_CTRL, "BSSGP-BVC", ts_pcu_bvc_fsm_id(nsei, bvci), exp); +} + +function f_init(float t_guard := 30.0) runs on test_CT { + var roro_integer bvci_unblocked; + var BssgpStatusIndication bsi; var integer i; if (g_initialized == true) { return; } g_initialized := true; - g_pcu[0].cfg := { - nsei := 96, - sgsn_role := false, - bvc := { { - bvci := 196, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13135}, - rac := 0 - }, - cell_id := 20960 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - } } - }; - g_pcu[1].cfg := { - nsei := 97, - sgsn_role := false, - bvc := { { - bvci := 210, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13200}, - rac := 0 - }, - cell_id := 20961 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - } } - }; - g_pcu[2].cfg := { - nsei := 98, - sgsn_role := false, - bvc := { { - bvci := 220, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13300}, - rac := 0 - }, - cell_id := 20962 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - } } - }; - g_sgsn[0].cfg := { - nsei := 101, - sgsn_role := true, - bvc := { - { - bvci := 196, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13135}, - rac := 0 - }, - cell_id := 20960 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - }, - { - bvci := 210, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13200}, - rac := 0 - }, - cell_id := 20961 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - }, - { - bvci := 220, - cell_id := { - ra_id := { - lai := { - mcc_mnc := mcc_mnc, lac := 13300}, - rac := 0 - }, - cell_id := 20962 - }, - depth := BSSGP_DECODE_DEPTH_BSSGP - } + g_Tguard.start(t_guard); + activate(as_gTguard(g_Tguard)); + + f_ipa_ctrl_start_client(mp_gbproxy_ip, mp_gbproxy_ctrl_port); + + var BssgpBvcConfigs bvcs := { }; + 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 */ + bvcs := bvcs & g_pcu[i].cfg.bvc; + } + + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + g_sgsn[i].cfg := { + nsei := mp_nsconfig_sgsn[i].nsei, + sgsn_role := true, + bvc := bvcs } - }; + } f_init_vty(); - f_init_gb_sgsn(g_sgsn[0], "GbProxy_Test-SGSN0", 0); + 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); - f_init_gb_pcu(g_pcu[0], "GbProxy_Test-PCU0", 0); - f_init_gb_pcu(g_pcu[1], "GbProxy_Test-PCU1", 1); - f_init_gb_pcu(g_pcu[2], "GbProxy_Test-PCU2", 2); + for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + f_init_gb_pcu(g_pcu[i], "GbProxy_Test", i); + } + + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + bvci_unblocked[i] := {}; + } + + /* wait until all BVC are unblocked on both sides */ + timer T := 15.0; + T.start; + alt { + /* TODO: We need to add more lines if NUM_SGSN increases. Activating default altsteps + * unfortunately doesn't work as we want to access the local variable bvci_unblocked. */ + [] as_count_unblocked4nse(0, bvci_unblocked); + [lengthof(g_sgsn) > 1] as_count_unblocked4nse(1, bvci_unblocked); + [] SGSN_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) { + repeat; + } + [] SGSN_MGMT.receive(BssgpResetIndication:?) { + repeat; + } + [] SGSN_MGMT.receive { + f_shutdown(__FILE__, __LINE__, fail, "Received unexpected message on SGSN_MGMT"); + } + + [] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, BVC_S_UNBLOCKED}) -> value bsi { + repeat; + } + [] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) { + repeat; + } + [] PCU_MGMT.receive(BssgpResetIndication:{0}) { + repeat; + } + [] PCU_MGMT.receive { + f_shutdown(__FILE__, __LINE__, fail, "Received unexpected message on PCU_MGMT"); + } + + [] T.timeout { + setverdict(fail, "Timeout waiting for unblock of all BVCs on SGSN side; ", + "unblocked so far: ", bvci_unblocked); + /* don't stop here but print below analysis */ + } + } + + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + /* iterate over list and check all BVCI */ + for (var integer j := 0; j < lengthof(g_sgsn[i].cfg.bvc); j := j+1) { + var BssgpBvci bvci := g_sgsn[i].cfg.bvc[j].bvci; + if (not ro_integer_contains(bvci_unblocked[i], bvci)) { + f_shutdown(__FILE__, __LINE__, fail, + log2str("SGSN ", i, " BVCI=", bvci, " was not unblocked during start-up")); + } + } + } + + /* Wait to ensure the gbproxy processed the RESET_ACK messages from the SGSN. + * Otherwise the state might still be WAIT_RESET_ACK */ + f_sleep(0.2); + + /* verify all SGSN-side BVC FSM in IUT are UNBLOCKED */ + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + f_bvc_fsm_ensure_state(mp_nsconfig_sgsn[i].nsei, 0, "UNBLOCKED"); + /* iterate over list and check all BVCI */ + for (var integer j := 0; j < lengthof(g_sgsn[i].cfg.bvc); j := j+1) { + var BssgpBvci bvci := g_sgsn[i].cfg.bvc[j].bvci; + f_bvc_fsm_ensure_state(mp_nsconfig_sgsn[i].nsei, bvci, "UNBLOCKED"); + } + } + /* verify all PCU-side BVC FSM in IUT are UNBLOCKED */ + for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + f_bvc_fsm_ensure_state(mp_nsconfig_pcu[i].nsei, 0, "UNBLOCKED"); + /* iterate over list and check all BVCI */ + for (var integer j := 0; j < lengthof(g_pcu[i].cfg.bvc); j := j+1) { + var BssgpBvci bvci := g_pcu[i].cfg.bvc[j].bvci; + f_bvc_fsm_ensure_state(mp_nsconfig_pcu[i].nsei, bvci, "UNBLOCKED"); + } + } + + /* 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 { - self.stop; + var integer i; + + /* To avoid a dynamic test case error we need to prevent messages arriving on unconnected + * ports. Waiting here ensures that any messages "in flight" will be delivered to the port + * before the component is shutdown and disconnected. */ + f_sleep(0.2); + + 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 pcu, GbInstances_SGSN sgsn, integer imsi_suffix, - float t_guard := 30.0) +function f_start_handler(void_fn fn, charstring id, integer imsi_suffix, float t_guard := 30.0, + integer sgsn_idx := 0, integer nri_idx := 0, boolean have_ptmsi := true) runs on test_CT return BSSGP_ConnHdlr { var BSSGP_ConnHdlr vc_conn; + var integer nri := mp_sgsn_nri[sgsn_idx][nri_idx]; + var OCT4 p_tmsi := f_gen_tmsi(imsi_suffix, nri_v := nri, nri_bitlen := mp_nri_bitlength); 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 := p_tmsi, p_tmsi_sig := omit, - tlli := f_gprs_tlli_random(), + tlli := f_gprs_tlli_from_tmsi(p_tmsi, TLLI_LOCAL), tlli_old := omit, ra := omit, - bssgp_cell_id := { pcu[0].cfg.bvc[0].cell_id, pcu[1].cfg.bvc[0].cell_id, pcu[2].cfg.bvc[0].cell_id }, + pcu := g_pcu, + sgsn := g_sgsn, + sgsn_idx := sgsn_idx, t_guard := t_guard }; + if (not have_ptmsi) { + pars.p_tmsi := omit; + } - vc_conn := BSSGP_ConnHdlr.create(id); - // PDU side - connect(vc_conn:PCU[0], pcu[0].vc_BSSGP_BVC[0]:BSSGP_SP); - connect(vc_conn:PCU_SIG[0], pcu[0].vc_BSSGP_BVC[0]:BSSGP_SP_SIG); - connect(vc_conn:PCU_PROC[0], pcu[0].vc_BSSGP_BVC[0]:BSSGP_PROC); - connect(vc_conn:PCU[1], pcu[1].vc_BSSGP_BVC[0]:BSSGP_SP); - connect(vc_conn:PCU_SIG[1], pcu[1].vc_BSSGP_BVC[0]:BSSGP_SP_SIG); - connect(vc_conn:PCU_PROC[1], pcu[1].vc_BSSGP_BVC[0]:BSSGP_PROC); - connect(vc_conn:PCU[2], pcu[2].vc_BSSGP_BVC[0]:BSSGP_SP); - connect(vc_conn:PCU_SIG[2], pcu[2].vc_BSSGP_BVC[0]:BSSGP_SP_SIG); - connect(vc_conn:PCU_PROC[2], pcu[2].vc_BSSGP_BVC[0]:BSSGP_PROC); - // SGSN side - connect(vc_conn:SGSN[0], sgsn[0].vc_BSSGP_BVC[0]:BSSGP_SP); - connect(vc_conn:SGSN_SIG[0], sgsn[0].vc_BSSGP_BVC[0]:BSSGP_SP_SIG); - connect(vc_conn:SGSN_PROC[0], sgsn[0].vc_BSSGP_BVC[0]:BSSGP_PROC); + vc_conn := BSSGP_ConnHdlr.create(id) alive; + log("Starting ", id, " for SGSN[", sgsn_idx, "], NRI=", nri, ", P-TMSI=", pars.p_tmsi, + ", TLLI=", pars.tlli, ", IMSI=", pars.imsi, " on component=", vc_conn); vc_conn.start(f_handler_init(fn, id, pars)); return vc_conn; } -private altstep as_Tguard() runs on BSSGP_ConnHdlr { - [] g_Tguard.timeout { - setverdict(fail, "Tguard timeout"); - mtc.stop; +function f_start_handlers(void_fn fn, charstring id, integer imsi_suffix, float t_guard := 30.0, + boolean have_ptmsi := true) +runs on test_CT +{ + var integer sgsn_idx, nri_idx; + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx:=sgsn_idx+1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx:=nri_idx+1) { + var integer extd_imsi_suffix := 1000*sgsn_idx + 100*nri_idx; + var BSSGP_ConnHdlr vc_conn; + vc_conn := f_start_handler(fn, id, extd_imsi_suffix, t_guard, sgsn_idx, nri_idx, + have_ptmsi); + /* Idea: we could also run them in parallel ? */ + vc_conn.done; + } + } +} + +/* 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 { + f_shutdown(__FILE__, __LINE__, fail, "Tguard timeout"); } } /* 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_Tguard()); + activate(as_gTguard(g_Tguard)); /* call the user-supplied test case function */ fn.apply(id); + + for (i := 0; i < NUM_SGSN; i := i+1) { + if (SGSN_PROC[i].checkstate("Connected")) { + f_client_unregister(g_pars.imsi, SGSN_PROC[i]) + } + } + + for (i := 0; i < NUM_PCU; i := i+1) { + if (PCU_PROC[i].checkstate("Connected")) { + f_client_unregister(g_pars.imsi, PCU_PROC[i]) + } + } +} + +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, boolean use_sig := false) runs on BSSGP_ConnHdlr { + var integer sgsn_idx := g_pars.sgsn_idx; + var PDU_BSSGP rx; + timer T := 2.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 { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Unexpected BSSGP on SGSN[", sgsn_idx, "] side: ", rx)); + } + [] SGSN_SIG[sgsn_idx].receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Unexpected SIG BSSGP on SGSN[", sgsn_idx, "] side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on SGSN[", sgsn_idx, "] side: ", exp_rx)); + } + } } +/* Send 'tx' from PCU; expect 'exp_rx' on _any_ SGSN */ +friend function f_pcu2any_sgsn(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx, + integer pcu_idx := 0, boolean use_sig := false) +runs on BSSGP_ConnHdlr return integer { + var integer rx_idx := -1; + var PDU_BSSGP rx; + timer T := 2.0; + + if (use_sig) { + PCU_SIG[pcu_idx].send(tx); + } else { + PCU_PTP[pcu_idx].send(tx); + } + + T.start; + alt { + [use_sig] any from SGSN_SIG.receive(exp_rx) -> @index value rx_idx { + setverdict(pass); + } + [not use_sig] any from SGSN_PTP.receive(exp_rx) -> @index value rx_idx { + setverdict(pass); + } + [] any from SGSN_PTP.receive(PDU_BSSGP:?) -> value rx @index value rx_idx { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Unexpected BSSGP on SGSN[", rx_idx, "] side: ", rx)); + } + [] any from SGSN_SIG.receive(PDU_BSSGP:?) -> value rx @index value rx_idx { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Unexpected SIG BSSGP on SGSN[", rx_idx, "] side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on SGSN side: ", exp_rx)); + } + } + return rx_idx; +} + +/* 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 pcu_idx := 0, boolean use_sig := false) runs on BSSGP_ConnHdlr { + var integer sgsn_idx := g_pars.sgsn_idx; + var PDU_BSSGP rx; + timer T := 2.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 { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on PCU side: ", rx)); + } + [] PCU_SIG[pcu_idx].receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected SIG BSSGP on PCU side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on PCU side: ", exp_rx)); + } + } +} + +/*********************************************************************** + * 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]; + var integer g_pcu_idx[NUM_PCU]; /* BVC index currently connected to G_PCU */ + port BSSGP_PT G_SGSN[NUM_SGSN]; + var integer g_sgsn_idx[NUM_SGSN]; /* BVC index currently connected to G_SGSN */ + port BSSGP_PT RIM_PCU[NUM_PCU]; + port BSSGP_PT RIM_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); + connect(self:RIM_SGSN[i], g_sgsn[i].vc_BSSGP:RIM); + } + for (i := 0; i < lengthof(g_pcu); i := i+1) { + connect(self:G_PCU[i], g_pcu[i].vc_BSSGP:GLOBAL); + connect(self:RIM_PCU[i], g_pcu[i].vc_BSSGP:RIM); + } +} + +/* 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) { + log("Connecting G_SGSN[", i, "] to BVCI=", g_sgsn[i].cfg.bvc[0].bvci); + connect(self:G_SGSN[i], g_sgsn[i].vc_BSSGP_BVC[0]:GLOBAL); + g_sgsn_idx[i] := 0; + } + for (i := 0; i < lengthof(g_pcu); i := i+1) { + log("Connecting G_PCU[", i, "] to BVCI=", g_pcu[i].cfg.bvc[0].bvci); + connect(self:G_PCU[i], g_pcu[i].vc_BSSGP_BVC[0]:GLOBAL); + g_pcu_idx[i] := 0; + } +} + +/* (re)connect G_SGSN[sgsn_idx] to a specific PTP BVCI */ +private function f_global_ptp_connect_sgsn_bvci(integer sgsn_idx, BssgpBvci bvci) runs on GlobalTest_CT +{ + var integer sgsn_bvc_idx := get_bvc_idx_for_bvci(g_sgsn[sgsn_idx], bvci); + var integer old_sgsn_bvc_idx := g_sgsn_idx[sgsn_idx]; + disconnect(self:G_SGSN[sgsn_idx], g_sgsn[sgsn_idx].vc_BSSGP_BVC[old_sgsn_bvc_idx]:GLOBAL); + connect(self:G_SGSN[sgsn_idx], g_sgsn[sgsn_idx].vc_BSSGP_BVC[sgsn_bvc_idx]:GLOBAL); + g_sgsn_idx[sgsn_idx] := sgsn_bvc_idx; +} + +/* (re)connect G_PCU[pcu_idx] to a specific PTP BVCI */ +private function f_global_ptp_connect_pcu_bvci(integer pcu_idx, BssgpBvci bvci) runs on GlobalTest_CT +{ + var integer pcu_bvc_idx := get_bvc_idx_for_bvci(g_pcu[pcu_idx], bvci); + var integer old_pcu_bvc_idx := g_pcu_idx[pcu_idx]; + disconnect(self:G_PCU[pcu_idx], g_pcu[pcu_idx].vc_BSSGP_BVC[old_pcu_bvc_idx]:GLOBAL); + connect(self:G_PCU[pcu_idx], g_pcu[pcu_idx].vc_BSSGP_BVC[pcu_bvc_idx]:GLOBAL); + g_pcu_idx[pcu_idx] := pcu_bvc_idx; +} + +/* 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 integer rx_idx; + var PDU_BSSGP rx; + timer T := 2.0; + + G_PCU[pcu_idx].send(tx); + T.start; + alt { + [] G_SGSN[sgsn_idx].receive(exp_rx) { + setverdict(pass); + } + [] any from G_SGSN.receive(exp_rx) -> @index value rx_idx { + setverdict(fail, "BSSGP arrived on wrong SGSN[", rx_idx, "] instead of SGSN[", sgsn_idx, "]"); + } + [] G_SGSN[sgsn_idx].receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on SGSN side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, log2str("Timeout waiting for BSSGP on SGSN side: ", exp_rx)); + } + } +} + +/* 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 integer rx_idx; + var PDU_BSSGP rx; + timer T := 2.0; + + G_SGSN[sgsn_idx].send(tx); + T.start; + alt { + [] G_PCU[pcu_idx].receive(exp_rx) { + setverdict(pass); + } + [] any from G_PCU.receive(exp_rx) -> @index value rx_idx { + setverdict(fail, "BSSGP arrived on wrong PCU[", rx_idx, "] instead of PCU[", pcu_idx, "]"); + } + [] G_PCU[pcu_idx].receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on PCU side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, log2str("Timeout waiting for BSSGP on PCU side: ", exp_rx)); + } + } +} + + /* TODO: * Detach without Attach * SM procedures without attach / RAU @@ -423,57 +1062,2743 @@ private function f_TC_BVC_bringup(charstring id) runs on BSSGP_ConnHdlr { } testcase TC_BVC_bringup() runs on test_CT { - var BSSGP_ConnHdlr vc_conn; f_init(); + f_start_handlers(refers(f_TC_BVC_bringup), testcasename(), 51); + f_cleanup(); +} + +testcase TC_BVC_bringup_conflicting() runs on test_CT { + var float t_guard := 15.0; + var BssgpStatusIndication bsi; + var integer i; + - vc_conn := f_start_handler(refers(f_TC_BVC_bringup), testcasename(), g_pcu, g_sgsn, 51); - vc_conn.done; + g_Tguard.start(t_guard); + activate(as_gTguard(g_Tguard)); + f_ipa_ctrl_start_client(mp_gbproxy_ip, mp_gbproxy_ctrl_port); + + var BssgpBvcConfigs bvcs := { }; + for (i := 0; i < lengthof(mp_gbconfigs); i := i+1) { + g_pcu[i].cfg := mp_gbconfigs[i]; + g_pcu[i].cfg.bvc[0].bvci := 23; + /* make sure all have a proper create_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 */ + bvcs := bvcs & g_pcu[i].cfg.bvc; + } + + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + g_sgsn[i].cfg := { + nsei := mp_nsconfig_sgsn[i].nsei, + sgsn_role := true, + bvc := bvcs + } + } + + 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 := 5.0; + T.start; + alt { + [] SGSN_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) { + repeat; + } + [] SGSN_MGMT.receive(BssgpResetIndication:?) { + repeat; + } + [] SGSN_MGMT.receive { + f_shutdown(__FILE__, __LINE__, fail, "Received unexpected message on SGSN_MGMT"); + } + [] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, BVC_S_UNBLOCKED}) -> value bsi { + repeat; + } + [] PCU_MGMT.receive(BssgpStatusIndication:{*, ?, ?}) { + repeat; + } + [] PCU_MGMT.receive(BssgpResetIndication:{0}) { + repeat; + } + [] PCU_MGMT.receive { + f_shutdown(__FILE__, __LINE__, fail, "Received unexpected message on PCU_MGMT"); + } + [] T.timeout { + } + } + + /* Wait to ensure the gbproxy processed the RESET_ACK messages from the SGSN. + * Otherwise the state might still be WAIT_RESET_ACK */ + f_sleep(0.2); + + /* Verify BVCs, but ignore conflicting BVCI 23 */ + /* verify SGSN-side BVC FSM in IUT are UNBLOCKED */ + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + f_bvc_fsm_ensure_state(mp_nsconfig_sgsn[i].nsei, 0, "UNBLOCKED"); + /* iterate over list and check all BVCI */ + for (var integer j := 0; j < lengthof(g_sgsn[i].cfg.bvc); j := j+1) { + var BssgpBvci bvci := g_sgsn[i].cfg.bvc[j].bvci; + if (bvci == 23) { + continue; + } + f_bvc_fsm_ensure_state(mp_nsconfig_sgsn[i].nsei, bvci, "UNBLOCKED"); + } + } + /* verify PCU-side BVC FSM in IUT are UNBLOCKED */ + for (i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + f_bvc_fsm_ensure_state(mp_nsconfig_pcu[i].nsei, 0, "UNBLOCKED"); + /* iterate over list and check all BVCI */ + for (var integer j := 0; j < lengthof(g_pcu[i].cfg.bvc); j := j+1) { + var BssgpBvci bvci := g_pcu[i].cfg.bvc[j].bvci; + if (bvci == 23) { + continue; + } + f_bvc_fsm_ensure_state(mp_nsconfig_pcu[i].nsei, bvci, "UNBLOCKED"); + } + } + + /* re-start guard timer after all BVCs are up, so it only counts the actual test case */ + g_Tguard.start(t_guard); + f_start_handlers(refers(f_TC_BVC_bringup), testcasename(), 51); 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, g_pars.bssgp_cell_id[ran_idx].ra_id)); + 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, g_pars.bssgp_cell_id[ran_idx].ra_id, ?)) -> value rx_pdu { + [] 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, g_pars.bssgp_cell_id[ran_idx].ra_id, ?)) -> value rx_pdu { - setverdict(fail, "SUSPEND-NACK in response to SUSPEND for TLLI ", g_pars.tlli); - mtc.stop; + [] PCU_SIG[ran_idx].receive(tr_BSSGP_SUSPEND_NACK(g_pars.tlli, bvcc.cell_id.ra_id, ?)) -> value rx_pdu { + f_shutdown(__FILE__, __LINE__, fail, + log2str("SUSPEND-NACK in response to SUSPEND for TLLI ", g_pars.tlli)); } [] T.timeout { - setverdict(fail, "No SUSPEND-ACK in response to SUSPEND for TLLI ", g_pars.tlli); - mtc.stop; + f_shutdown(__FILE__, __LINE__, fail, + log2str("No SUSPEND-ACK in response to SUSPEND for TLLI ", g_pars.tlli)); } } 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, ?)) { + f_shutdown(__FILE__, __LINE__, fail, + log2str("RESUME-NACK in response to RESUME for TLLI ", g_pars.tlli)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("No RESUME-ACK in response to SUSPEND for TLLI ", g_pars.tlli)); + } + } +} + + +/* send uplink-unitdata of a variety of different sizes; expect it to show up on SGSN */ +private function f_TC_ul_unitdata(charstring id) runs on BSSGP_ConnHdlr { + var integer ran_idx := 0; + var BssgpBvcConfig bvcc := g_pars.pcu[ran_idx].cfg.bvc[0]; + var integer i; + + for (i := 0; i < max_fr_info_size-4; i := i+4) { + var octetstring payload := f_rnd_octstring(i); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_UL_UD(g_pars.tlli, bvcc.cell_id, payload); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_UL_UD(g_pars.tlli, bvcc.cell_id, payload); + + log("UL-UNITDATA(payload_size=", i); + f_pcu2sgsn(pdu_tx, pdu_rx); + } + setverdict(pass); +} + +testcase TC_ul_unitdata() runs on test_CT +{ + f_init(60.0); + f_start_handlers(refers(f_TC_ul_unitdata), testcasename(), 1); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + + +/* send uplink-unitdata of a variety of different sizes; expect it to show up on the only connected SGSN */ +private function f_TC_ul_unitdata_pool_failure(charstring id) runs on BSSGP_ConnHdlr { + var integer ran_idx := 0; + var BssgpBvcConfig bvcc := g_pars.pcu[ran_idx].cfg.bvc[0]; + var integer i; + + /* All data should arrive at the one SGSN that is still up */ + g_pars.sgsn_idx := 0; + + for (i := 0; i < max_fr_info_size-4; i := i+4) { + var octetstring payload := f_rnd_octstring(i); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_UL_UD(g_pars.tlli, bvcc.cell_id, payload); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_UL_UD(g_pars.tlli, bvcc.cell_id, payload); + + log("UL-UNITDATA(payload_size=", i); + f_pcu2sgsn(pdu_tx, pdu_rx); + } + setverdict(pass); +} + +private function f_disable_ns_pcu(integer pcu_idx) runs on test_CT +{ + var integer i; + + connect(self:NS_CTRL, g_pcu[pcu_idx].vc_NS:NS_CTRL); + for (i := 0; i < lengthof(mp_nsconfig_pcu[pcu_idx].nsvc); i := i + 1) { + var uint16_t nsvci := mp_nsconfig_pcu[pcu_idx].nsvc[i].nsvci; + var NsDisableVcRequest tx_disar; + tx_disar.nsvci := nsvci; + log(tx_disar); + NS_CTRL.send(tx_disar); + } + disconnect(self:NS_CTRL, g_pcu[pcu_idx].vc_NS:NS_CTRL); +} + +testcase TC_ul_unitdata_pool_failure() runs on test_CT +{ + var integer i; + var integer j; + + f_init(60.0); + + for (i := 1; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + connect(self:NS_CTRL, g_sgsn[i].vc_NS:NS_CTRL); + for (j := 0; j < lengthof(mp_nsconfig_sgsn[i].nsvc); j := j+1) { + var uint16_t nsvci := mp_nsconfig_sgsn[i].nsvc[j].nsvci; + var NsDisableVcRequest tx_disar; + tx_disar.nsvci := nsvci; + NS_CTRL.send(tx_disar); + } + disconnect(self:NS_CTRL, g_sgsn[i].vc_NS:NS_CTRL); + } + /* Wait until gbproxy notices that the NSVCs are down */ + f_sleep(15.0); + + f_start_handlers(refers(f_TC_ul_unitdata_pool_failure), testcasename(), 1); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +/* send downlink-unitdata of a variety of different sizes; expect it to show up on PCU */ +private function f_TC_dl_unitdata(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + + for (i := 0; i < max_fr_info_size-4; i := i+4) { + var octetstring payload := f_rnd_octstring(i); + var template (value) PDU_BSSGP pdu_tx := + ts_BSSGP_DL_UD(g_pars.tlli, payload, omit, ts_BSSGP_IMSI(g_pars.imsi)); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := + tr_BSSGP_DL_UD(g_pars.tlli, payload, tr_BSSGP_IMSI(g_pars.imsi)); + + log("DL-UNITDATA(payload_size=", i, ")"); + f_sgsn2pcu(pdu_tx, pdu_rx); + } + setverdict(pass); +} + +testcase TC_dl_unitdata() runs on test_CT +{ + f_init(60.0); + f_start_handlers(refers(f_TC_dl_unitdata), testcasename(), 2); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +private function f_TC_ra_capability(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + + for (i := 0; i < 10; i := i+1) { + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RA_CAP(g_pars.tlli, { ts_RaCapRec_BSSGP }); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RA_CAP(g_pars.tlli, { tr_RaCapRec_BSSGP }) + + f_sgsn2pcu(pdu_tx, pdu_rx); + } + setverdict(pass); +} +testcase TC_ra_capability() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_ra_capability), testcasename(), 3); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +private function f_TC_ra_capability_upd(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + var OCT1 tag; + for (i := 0; i < 10; i := i+1) { + tag := int2oct(23 + i, 1); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RA_CAP_UPD(g_pars.tlli, tag); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RA_CAP_UPD(g_pars.tlli, tag) + + f_pcu2sgsn(pdu_tx, pdu_rx); + + pdu_tx := ts_BSSGP_RA_CAP_UPD_ACK(g_pars.tlli, tag, '42'O); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_RA_CAP_UPD_ACK(g_pars.tlli, tag, '42'O) + + f_sgsn2pcu(pdu_tx, pdu_rx); + } + setverdict(pass); +} +testcase TC_ra_capability_upd() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_ra_capability_upd), testcasename(), 4); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +private function f_TC_radio_status(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + var BssgpRadioCause cause := BSSGP_RADIO_CAUSE_CONTACT_LOST; + for (i := 0; i < 10; i := i+1) { + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RADIO_STATUS(g_pars.tlli, cause); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RADIO_STATUS(g_pars.tlli, cause) + + f_pcu2sgsn(pdu_tx, pdu_rx); + } + setverdict(pass); +} +testcase TC_radio_status() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_radio_status), testcasename(), 5); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +private function f_TC_radio_status_tmsi(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + var BssgpRadioCause cause := BSSGP_RADIO_CAUSE_CONTACT_LOST; + for (i := 0; i < 10; i := i+1) { + var integer tmsi_int := oct2int(g_pars.p_tmsi); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RADIO_STATUS(omit, cause, tmsi_int); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RADIO_STATUS(omit, cause, tmsi_int); + f_pcu2sgsn(pdu_tx, pdu_rx); + } + setverdict(pass); +} +testcase TC_radio_status_tmsi() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_radio_status_tmsi), testcasename(), 5); + f_cleanup(); +} + +private function f_TC_radio_status_imsi(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + var BssgpRadioCause cause := BSSGP_RADIO_CAUSE_CONTACT_LOST; + for (i := 0; i < 10; i := i+1) { + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RADIO_STATUS(omit, cause, imsi := g_pars.imsi); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RADIO_STATUS(omit, cause, imsi := g_pars.imsi); + f_pcu2any_sgsn(pdu_tx, pdu_rx); + } + setverdict(pass); +} +testcase TC_radio_status_imsi() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_radio_status_imsi), testcasename(), 5); + f_cleanup(); +} + + + +private function f_suspend_one(integer sgsn_idx, integer nri_idx, integer pcu_idx, integer bvc_idx, + integer suffix) +runs on GlobalTest_CT +{ + var RoutingAreaIdentification ra_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id.ra_id; + var OCT4 p_tmsi := f_gen_tmsi(suffix, nri_v := mp_sgsn_nri[sgsn_idx][nri_idx], + nri_bitlen := mp_nri_bitlength); + var OCT4 tlli := f_gprs_tlli_from_tmsi(p_tmsi, TLLI_LOCAL); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_SUSPEND(tlli, ra_id); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_SUSPEND(tlli, ra_id); + f_global_pcu2sgsn(pdu_tx, pdu_rx, pcu_idx, sgsn_idx); + + pdu_tx := ts_BSSGP_SUSPEND_ACK(tlli, ra_id, int2oct(suffix, 1)); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_SUSPEND_ACK(tlli, ra_id, int2oct(suffix, 1)); + f_global_sgsn2pcu(pdu_tx, pdu_rx, sgsn_idx, pcu_idx); + + pdu_tx := ts_BSSGP_SUSPEND(tlli, ra_id); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_SUSPEND(tlli, ra_id); + f_global_pcu2sgsn(pdu_tx, pdu_rx, pcu_idx, sgsn_idx); + + /* These messages are simple passed through so just also test sending NACK */ + pdu_tx := ts_BSSGP_SUSPEND_NACK(tlli, ra_id, BSSGP_CAUSE_UNKNOWN_MS); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_SUSPEND_NACK(tlli, ra_id, BSSGP_CAUSE_UNKNOWN_MS); + f_global_sgsn2pcu(pdu_tx, pdu_rx, sgsn_idx, pcu_idx); +} + +private function f_TC_suspend(integer sgsn_idx, integer nri_idx, integer pcu_idx, integer bvc_idx) +runs on GlobalTest_CT { + var integer i; + + for (i := 0; i < 10; i := i+1) { + f_suspend_one(sgsn_idx, nri_idx, pcu_idx, bvc_idx, suffix := i); + } + setverdict(pass); +} +testcase TC_suspend() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + f_init(); + f_global_init(); + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx+1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx+1) { + f_TC_suspend(sgsn_idx, nri_idx, pcu_idx:=0, bvc_idx:=0); + } + } + f_cleanup(); +} + +private function f_resume_one(integer sgsn_idx, integer nri_idx, integer pcu_idx, integer bvc_idx, + integer suffix) +runs on GlobalTest_CT +{ + var RoutingAreaIdentification ra_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id.ra_id; + var OCT4 p_tmsi := f_gen_tmsi(suffix, nri_v := mp_sgsn_nri[sgsn_idx][nri_idx], + nri_bitlen := mp_nri_bitlength); + var OCT4 tlli := f_gprs_tlli_from_tmsi(p_tmsi, TLLI_LOCAL); + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_RESUME(tlli, ra_id, int2oct(suffix, 1)); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_RESUME(tlli, ra_id, int2oct(suffix, 1)); + f_global_pcu2sgsn(pdu_tx, pdu_rx, pcu_idx, sgsn_idx); + + pdu_tx := ts_BSSGP_RESUME_ACK(tlli, ra_id); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_RESUME_ACK(tlli, ra_id); + f_global_sgsn2pcu(pdu_tx, pdu_rx, sgsn_idx, pcu_idx); + + pdu_tx := ts_BSSGP_RESUME(tlli, ra_id, int2oct(suffix, 1)); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_RESUME(tlli, ra_id, int2oct(suffix, 1)); + f_global_pcu2sgsn(pdu_tx, pdu_rx, pcu_idx, sgsn_idx); + + /* These messages are simple passed through so just also test sending NACK */ + pdu_tx := ts_BSSGP_RESUME_NACK(tlli, ra_id, BSSGP_CAUSE_UNKNOWN_MS); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_RESUME_NACK(tlli, ra_id, BSSGP_CAUSE_UNKNOWN_MS); + f_global_sgsn2pcu(pdu_tx, pdu_rx, sgsn_idx, pcu_idx); +} + +private function f_TC_resume(integer sgsn_idx, integer nri_idx, integer pcu_idx, integer bvc_idx) +runs on GlobalTest_CT { + var integer i; + + for (i := 0; i < 10; i := i+1) { + f_resume_one(sgsn_idx, nri_idx, pcu_idx, bvc_idx, suffix := i); + } + setverdict(pass); +} +testcase TC_resume() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + f_init(); + f_global_init(); + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx+1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx+1) { + f_TC_resume(sgsn_idx, nri_idx, pcu_idx:=0, bvc_idx:=0); + } + } + f_cleanup(); +} + +/* test the load-sharing between multiple NS-VC on the BSS side */ +private function f_TC_dl_ud_unidir(charstring id) runs on BSSGP_ConnHdlr { + var integer i; + + for (i := 0; i < 10; i := i+1) { + var octetstring payload := f_rnd_octstring(i); + var template (value) PDU_BSSGP pdu_tx := + ts_BSSGP_DL_UD(g_pars.tlli, payload, omit, ts_BSSGP_IMSI(g_pars.imsi)); + SGSN_PTP[g_pars.sgsn_idx].send(pdu_tx); + } + setverdict(pass); +} + +private function f_TC_load_sharing_dl(integer sgsn_idx) runs on test_CT_NS +{ + const integer num_ue := 10; + var BSSGP_ConnHdlr vc_conn[num_ue]; + /* all BVC are now fully brought up. We disconnect BSSGP from NS on the BSS + * side so we get the raw NsUnitdataIndication and hence observe different + * NSVCI */ + disconnect(g_pcu[0].vc_NS:NS_SP, g_pcu[0].vc_BSSGP:BSCP); + connect(g_pcu[0].vc_NS:NS_SP, self:NS); + + /* there may still be some NS-VCs coming up? After all, the BVC-RESET succeeds after the first + * of the NS-VC is ALIVE/UNBLOCKED */ + f_sleep(3.0); + + /* start parallel components generating DL-UNITDATA from the SGSN side */ + for (var integer i:= 0; i < num_ue; i := i+1) { + vc_conn[i] := f_start_handler(refers(f_TC_dl_ud_unidir), testcasename(), + 5+i, 30.0, sgsn_idx); + } + + /* now start counting all the messages that were queued before */ + /* TODO: We have a hard-coded assumption of 4 NS-VC in one NSE/NS-VCG here! */ + var ro_integer rx_count := { 0, 0, 0, 0 }; + timer T := 2.0; + T.start; + alt { + [] as_NsUdiCount(0, rx_count); + [] as_NsUdiCount(1, rx_count); + [] as_NsUdiCount(2, rx_count); + [] as_NsUdiCount(3, rx_count); + [] NS.receive(NsUnitdataIndication:{0,?,?,*,*}) { repeat; } /* signaling BVC */ + [] NS.receive(NsStatusIndication:?) { repeat; } + [] NS.receive { + f_shutdown(__FILE__, __LINE__, fail, "Rx unexpected NS"); + } + [] T.timeout { + } + } + for (var integer i := 0; i < lengthof(rx_count); i := i+1) { + log("Rx on NSVCI ", mp_nsconfig_pcu[0].nsvc[i].nsvci, ": ", rx_count[i]); + if (rx_count[i] == 0) { + setverdict(fail, "Data not shared over all NSVC"); + } + } +} + +testcase TC_load_sharing_dl() runs on test_CT_NS +{ + var integer sgsn_idx, nri_idx; + f_init(); + for (sgsn_idx:=0; sgsn_idx < NUM_SGSN; sgsn_idx:=sgsn_idx+1) { + f_TC_load_sharing_dl(sgsn_idx); + } + setverdict(pass); +} +private altstep as_NsUdiCount(integer nsvc_idx, inout ro_integer roi) runs on test_CT_NS { + var NsUnitdataIndication udi; + var BssgpBvcConfig bvcc := g_pcu[0].cfg.bvc[0]; + [] NS.receive(NsUnitdataIndication:{bvcc.bvci, g_pcu[0].cfg.nsei, mp_nsconfig_pcu[0].nsvc[nsvc_idx].nsvci, *, *}) -> value udi { + roi[nsvc_idx] := roi[nsvc_idx] + 1; + repeat; + } +} +type component test_CT_NS extends test_CT { + port NS_PT NS; +}; + + +/*********************************************************************** + * PAGING PS procedure + ***********************************************************************/ + +private function f_send_paging_ps(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false) +runs on BSSGP_ConnHdlr return template (present) PDU_BSSGP { + var template (value) PDU_BSSGP pdu_tx; + var template (present) PDU_BSSGP pdu_rx; + /* we always specify '0' as BVCI in the templates below, as we override it with + * 'p4' later anyway */ + pdu_rx := tr_BSSGP_PS_PAGING(0); + pdu_rx.pDU_BSSGP_PAGING_PS.iMSI := tr_BSSGP_IMSI(g_pars.imsi); + if (ispresent(g_pars.p_tmsi)) { + pdu_tx := ts_BSSGP_PS_PAGING_PTMSI(0, g_pars.imsi, oct2int(g_pars.p_tmsi)); + pdu_rx.pDU_BSSGP_PAGING_PS.pTMSI := tr_BSSGP_TMSI(oct2int(g_pars.p_tmsi)); + } else { + pdu_tx := ts_BSSGP_PS_PAGING_IMSI(0, g_pars.imsi); + pdu_rx.pDU_BSSGP_PAGING_PS.pTMSI := omit; + } + pdu_tx.pDU_BSSGP_PAGING_PS.paging_Field4 := p4; + pdu_rx.pDU_BSSGP_PAGING_PS.paging_Field4 := p4; + if (use_sig == false) { + SGSN_PTP[sgsn_idx].send(pdu_tx); + } else { + SGSN_SIG[sgsn_idx].send(pdu_tx); + } + return pdu_rx; +} + +/* send paging defined by 'p4' on given SGSN-side index (ptp or signaling) and expect one paging to arrive on + * specified PCU index */ +private function f_send_paging_ps_exp_one_bss(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false,integer pcu_idx := 0) +runs on BSSGP_ConnHdlr { + var template (present) PDU_BSSGP exp_rx; + var boolean test_done := false; + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + exp_rx := f_send_paging_ps(p4, sgsn_idx, use_sig); + /* Expect paging to propagate to the one BSS addressed by the BVCI only */ + timer T := 2.0; + T.start; + alt { + [not use_sig and not test_done] PCU_PTP[pcu_idx].receive(exp_rx) { + setverdict(pass); + test_done := true; + repeat; + } + [not use_sig] PCU_SIG[pcu_idx].receive(exp_rx) { + setverdict(fail, "Received paging on SIGNALING BVC, expected PTP BVC"); + } + [use_sig and not test_done] PCU_SIG[pcu_idx].receive(exp_rx) { + setverdict(pass); + test_done := true; + repeat; + } + [use_sig] PCU_PTP[pcu_idx].receive(exp_rx) { + setverdict(fail, "Received paging on PTP BVC, expected SIGNALING BVC"); + } + [] any from PCU_PTP.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_SIG.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_PTP.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging than expected received PTP BVC"); + } + [] any from PCU_SIG.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging than expected on SIGNALING BVC"); + } + [not test_done] T.timeout { + setverdict(fail, "Timeout waiting for paging"); + } + [test_done] T.timeout; + } +} + +/* send a PS-PAGING but don't expect it to show up on any PTP or SIG BVC */ +private function f_send_paging_ps_exp_no_bss(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false) +runs on BSSGP_ConnHdlr { + var template (present) PDU_BSSGP exp_rx; + exp_rx := f_send_paging_ps(p4, sgsn_idx, use_sig); + /* Expect paging to propagate to no BSS */ + timer T := 2.0; + T.start; + alt { + [] any from PCU_PTP.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_SIG.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_PTP.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging received on PTP BVC"); + } + [] any from PCU_SIG.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging received on SIGNALING BVC"); + } + [] T.timeout { + setverdict(pass); + } + } +} + +private function f_TC_paging_ps_ptp_bss(charstring id) runs on BSSGP_ConnHdlr +{ + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4BssArea, g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_bss() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_bss), testcasename(), 9); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for Location Area */ +private function f_TC_paging_ps_ptp_lac(charstring id) runs on BSSGP_ConnHdlr +{ + var template (present) PDU_BSSGP exp_rx; + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4LAC(pcu_bvc_cfg[0].cell_id.ra_id.lai), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_lac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_lac), testcasename(), 10); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for unknown Location Area */ +private function f_TC_paging_ps_ptp_lac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var GSM_Types.LocationAreaIdentification unknown_la := { + mcc_mnc := '567F99'H, + lac := 33333 + }; + /* as it's sent on the PTP BVC, we expect it to pass even for unknown LAC */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4LAC(unknown_la), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_lac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_lac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for Routeing Area */ +private function f_TC_paging_ps_ptp_rac(charstring id) runs on BSSGP_ConnHdlr +{ + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4RAC(pcu_bvc_cfg[0].cell_id.ra_id), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_rac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_rac), testcasename(), 11); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for unknown Routeing Area */ +private function f_TC_paging_ps_ptp_rac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var RoutingAreaIdentification unknown_ra := { + lai := { + mcc_mnc := '567F99'H, + lac := 33333 + }, + rac := 254 + }; + /* as it's sent on the PTP BVC, we expect it to pass even for unknown RAC */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4RAC(unknown_ra), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_rac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_rac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for BVCI (one cell) */ +private function f_TC_paging_ps_ptp_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + /* this should be the normal case for MS in READY MM state after a lower layer failure */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_bvci), testcasename(), 12); + f_cleanup(); +} + + +/* PS-PAGING on PTP-BVC for BVCI (one cell) using IMSI only (no P-TMSI allocated) */ +testcase TC_paging_ps_ptp_bvci_imsi() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_bvci), testcasename(), 12, have_ptmsi:=false); + f_cleanup(); +} + +/* Rejected PS-PAGING on PTP-BVC for BVCI (one cell) */ +testcase TC_paging_ps_reject_ptp_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_reject_ptp_bvci), testcasename(), 16); + f_cleanup(); +} + +/* Rejected PS-PAGING on PTP-BVC for BVCI (one cell) using IMSI only (no P-TMSI allocated) */ +private function f_TC_paging_ps_reject_ptp_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + /* first send the PS-PAGING from SGSN -> PCU */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), g_pars.sgsn_idx, false, 0); + /* then simulate the PS-PAGING-REJECT from the PCU */ + f_send_paging_ps_rej(use_sig:=false); +} +testcase TC_paging_ps_reject_ptp_bvci_imsi() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_reject_ptp_bvci), testcasename(), 16, have_ptmsi:=false); + f_cleanup(); +} + +/* PS-PAGING on PTP-BVC for unknown BVCI */ +private function f_TC_paging_ps_ptp_bvci_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + /* as it's sent on the PTP BVC, we expect it to pass even for unknown BVCI */ + f_send_paging_ps_exp_one_bss(ts_BssgpP4Bvci(33333), g_pars.sgsn_idx, false, 0); +} +testcase TC_paging_ps_ptp_bvci_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_ptp_bvci_unknown), testcasename(), 11); + f_cleanup(); +} + +/* DUMMY PAGING PS on PTP BVC */ +private function f_TC_dummy_paging_ps_ptp(charstring id) runs on BSSGP_ConnHdlr +{ + f_sgsn2pcu(ts_BSSGP_DUMMY_PAGING_PS(g_pars.imsi, omit), + tr_BSSGP_DUMMY_PAGING_PS(g_pars.imsi, omit), use_sig := false); + f_pcu2sgsn(ts_BSSGP_DUMMY_PAGING_PS_RESP(g_pars.imsi, 1, 5), + tr_BSSGP_DUMMY_PAGING_PS_RESP(g_pars.imsi, 1, 5), use_sig := false) +} +testcase TC_dummy_paging_ps_ptp() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_dummy_paging_ps_ptp), testcasename(), 11); + f_cleanup(); +} + +/* altstep for expecting BSSGP PDU on signaling BVC of given pcu_idx + storing in 'roi' */ +private altstep as_paging_sig_pcu(integer pcu_idx, template (present) PDU_BSSGP exp_rx, inout ro_integer roi) +runs on BSSGP_ConnHdlr { +[] PCU_SIG[pcu_idx].receive(exp_rx) { + if (ro_integer_contains(roi, pcu_idx)) { + setverdict(fail, "Received multiple paging on same SIG BVC"); + } + roi := roi & { pcu_idx }; + repeat; + } +[] PCU_PTP[pcu_idx].receive(exp_rx) { + setverdict(fail, "Received paging on PTP BVC, expected SIGNALING BVC"); + } +[] PCU_SIG[pcu_idx].receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging than expected received SIGNALING BVC"); + } +[] PCU_PTP[pcu_idx].receive(PDU_BSSGP:{pDU_BSSGP_PAGING_PS:=?}) { + setverdict(fail, "Different Paging than expected received PTP BVC"); + } +} + +type record of default ro_default; + +/* send PS-PAGING on SIG BVC, expect it to arrive on given list of PCU indexes */ +private function f_send_paging_ps_exp_multi(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + ro_integer exp_on_pcu_idx) runs on BSSGP_ConnHdlr +{ + var template (present) PDU_BSSGP exp_rx; + exp_rx := f_send_paging_ps(p4, sgsn_idx, true); + + /* FIXME: make sure the relevant BVCs/BSS are connected to the ports! */ + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + var default d := activate(as_paging_sig_pcu(i, exp_rx, g_roi)); + defaults := defaults & { d }; + } + f_sleep(2.0); + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + log("Paging received on PCU ", g_roi); + + for (var integer i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + var boolean rx_on_i := ro_integer_contains(g_roi, i); + var boolean exp_on_i := ro_integer_contains(exp_on_pcu_idx, i); + if (exp_on_i and not rx_on_i) { + setverdict(fail, "PS-PAGING not received on ", mp_nsconfig_pcu[i].nsei); + } + if (not exp_on_i and rx_on_i) { + setverdict(fail, "PS-PAGING not expected but received on ", mp_nsconfig_pcu[i].nsei); + } + } + setverdict(pass); +} + +/* Send PAGING-PS-REJECT on SIG BVC, expect it to arrive on the "right" SGSN */ +private function f_send_paging_ps_rej(boolean use_sig := true, integer pcu_idx := 0) runs on BSSGP_ConnHdlr +{ + var template (value) PDU_BSSGP pdu_tx; + var template (present) PDU_BSSGP exp_rx; + var PDU_BSSGP pdu_rx; timer T := 5.0; - PCU_SIG[ran_idx].send(ts_BSSGP_RESUME(g_pars.tlli, g_pars.bssgp_cell_id[ran_idx].ra_id, susp_ref)); + var template (omit) GsmTmsi tmsi_int := omit; + + if (ispresent(g_pars.p_tmsi)) { + tmsi_int := oct2int(g_pars.p_tmsi); + } + + pdu_tx := ts_BSSGP_PAGING_PS_REJ(g_pars.imsi, 23, 42, tmsi_int); + exp_rx := tr_BSSGP_PAGING_PS_REJ(g_pars.imsi, 23, 42, tmsi_int); + + if (use_sig) { + PCU_SIG[pcu_idx].send(pdu_tx); + } else { + PCU_PTP[pcu_idx].send(pdu_tx); + } + T.start; + alt { + [use_sig] SGSN_SIG[g_pars.sgsn_idx].receive(exp_rx) -> value pdu_rx { + setverdict(pass); + } + [use_sig] SGSN_SIG[g_pars.sgsn_idx].receive { + setverdict(fail, "Unexpected PDU on SGSN"); + } + [use_sig] any from SGSN_SIG.receive(exp_rx) -> value pdu_rx { + setverdict(fail, "PAGING-PS-REJECT arrived on wrong SGSN"); + } + [not use_sig] SGSN_PTP[g_pars.sgsn_idx].receive(exp_rx) -> value pdu_rx { + setverdict(pass); + } + [not use_sig] SGSN_PTP[g_pars.sgsn_idx].receive { + setverdict(fail, "Unexpected PDU on SGSN"); + } + [not use_sig] any from SGSN_PTP.receive(exp_rx) -> value pdu_rx { + setverdict(fail, "PAGING-PS-REJECT arrived on wrong SGSN"); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for PAGING-PS-REJECT"); + } + } +} + +/* PS-PAGING on SIG-BVC for BSS Area */ +private function f_TC_paging_ps_sig_bss(charstring id) runs on BSSGP_ConnHdlr +{ + /* we expect the paging to arrive on all three NSE */ + f_send_paging_ps_exp_multi(ts_BssgpP4BssArea, g_pars.sgsn_idx, {0, 1, 2}); +} +testcase TC_paging_ps_sig_bss() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_bss), testcasename(), 13); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for Location Area */ +private function f_TC_paging_ps_sig_lac(charstring id) runs on BSSGP_ConnHdlr +{ + /* The first LAC (13135) is shared by all three NSEs */ + f_send_paging_ps_exp_multi(ts_BssgpP4LAC(pcu_bvc_cfg[0].cell_id.ra_id.lai), g_pars.sgsn_idx, {0, 1, 2}); + /* Reset state */ + g_roi := {}; + /* Make LAC (13300) available on pcu index 2 */ + f_connect_to_pcu_bvc(port_idx := 2, nse_idx := 2, bvc_idx := 1); + f_send_paging_ps_exp_multi(ts_BssgpP4LAC(pcu_bvc_cfg[2].cell_id.ra_id.lai), g_pars.sgsn_idx, {2}); +} +testcase TC_paging_ps_sig_lac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_lac), testcasename(), 14); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for unknown Location Area */ +private function f_TC_paging_ps_sig_lac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var GSM_Types.LocationAreaIdentification unknown_la := { + mcc_mnc := '567F99'H, + lac := 33333 + }; + f_send_paging_ps_exp_no_bss(ts_BssgpP4LAC(unknown_la), g_pars.sgsn_idx, true); +} +testcase TC_paging_ps_sig_lac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_lac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for Routeing Area */ +private function f_TC_paging_ps_sig_rac(charstring id) runs on BSSGP_ConnHdlr +{ + /* Only PCU index 0 has a matching BVC with the RA ID */ + f_send_paging_ps_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[0].cell_id.ra_id), g_pars.sgsn_idx, {0}); + g_roi := {}; + /* PCU index 1 and 2 have a matching BVC with the RA ID */ + f_send_paging_ps_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[2].cell_id.ra_id), g_pars.sgsn_idx, {1, 2}); + g_roi := {}; + /* PCU index 2 has two matching BVCs with the RA ID */ + f_connect_to_pcu_bvc(port_idx := 2, nse_idx := 2, bvc_idx := 1); + f_send_paging_ps_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[2].cell_id.ra_id), g_pars.sgsn_idx, {2}); +} +testcase TC_paging_ps_sig_rac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_rac), testcasename(), 15); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for unknown Routeing Area */ +private function f_TC_paging_ps_sig_rac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var RoutingAreaIdentification unknown_ra := { + lai := { + mcc_mnc := '567F99'H, + lac := 33333 + }, + rac := 254 + }; + f_send_paging_ps_exp_no_bss(ts_BssgpP4RAC(unknown_ra), g_pars.sgsn_idx, true); +} +testcase TC_paging_ps_sig_rac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_rac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for BVCI (one cell) */ +private function f_TC_paging_ps_sig_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + f_send_paging_ps_exp_multi(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), g_pars.sgsn_idx, {0}); +} +testcase TC_paging_ps_sig_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_bvci), testcasename(), 16); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for BVCI (one cell) using IMSI only (no P-TMSI allocated) */ +testcase TC_paging_ps_sig_bvci_imsi() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_bvci), testcasename(), 16, have_ptmsi:=false); + f_cleanup(); +} + +/* Rejected PS-PAGING on SIG-BVC for BVCI (one cell) */ +private function f_TC_paging_ps_reject_sig_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + /* first send the PS-PAGING from SGSN -> PCU */ + f_send_paging_ps_exp_multi(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), g_pars.sgsn_idx, {0}); + /* then simulate the PS-PAGING-REJECT from the PCU */ + f_send_paging_ps_rej(use_sig:=true); + +} +testcase TC_paging_ps_reject_sig_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_reject_sig_bvci), testcasename(), 16); + f_cleanup(); +} + +/* Rejected PS-PAGING on SIG-BVC for BVCI (one cell) using IMSI only (no P-TMSI allocated) */ +testcase TC_paging_ps_reject_sig_bvci_imsi() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_reject_sig_bvci), testcasename(), 16, have_ptmsi:=false); + f_cleanup(); +} + +/* PS-PAGING on SIG-BVC for unknown BVCI */ +private function f_TC_paging_ps_sig_bvci_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + f_send_paging_ps_exp_no_bss(ts_BssgpP4Bvci(33333), g_pars.sgsn_idx, true); +} +testcase TC_paging_ps_sig_bvci_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_ps_sig_bvci_unknown), testcasename(), 11); + f_cleanup(); +} + +/* DUMMY PAGING PS on SIG BVC */ +private function f_TC_dummy_paging_ps_sig(charstring id) runs on BSSGP_ConnHdlr +{ + f_sgsn2pcu(ts_BSSGP_DUMMY_PAGING_PS(g_pars.imsi, omit), + tr_BSSGP_DUMMY_PAGING_PS(g_pars.imsi, omit), use_sig := true); + f_pcu2sgsn(ts_BSSGP_DUMMY_PAGING_PS_RESP(g_pars.imsi, 1, 5), + tr_BSSGP_DUMMY_PAGING_PS_RESP(g_pars.imsi, 1, 5), use_sig := true) +} +testcase TC_dummy_paging_ps_sig() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_dummy_paging_ps_sig), testcasename(), 11); + f_cleanup(); +} + + + +/*********************************************************************** + * PAGING CS procedure + ***********************************************************************/ + +private function f_send_paging_cs(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false) +runs on BSSGP_ConnHdlr return template (present) PDU_BSSGP { + var template (value) PDU_BSSGP pdu_tx; + var template (present) PDU_BSSGP pdu_rx; + /* we always specify '0' as BVCI in the templates below, as we override it with + * 'p4' later anyway */ + pdu_rx := tr_BSSGP_CS_PAGING(0); + pdu_rx.pDU_BSSGP_PAGING_CS.iMSI := tr_BSSGP_IMSI(g_pars.imsi); + if (ispresent(g_pars.p_tmsi)) { + pdu_tx := ts_BSSGP_CS_PAGING_PTMSI(0, g_pars.imsi, oct2int(g_pars.p_tmsi)); + pdu_rx.pDU_BSSGP_PAGING_CS.tMSI := tr_BSSGP_TMSI(oct2int(g_pars.p_tmsi)); + } else { + pdu_tx := ts_BSSGP_CS_PAGING_IMSI(0, g_pars.imsi); + pdu_rx.pDU_BSSGP_PAGING_CS.tMSI := omit; + } + pdu_tx.pDU_BSSGP_PAGING_CS.paging_Field4 := p4; + pdu_rx.pDU_BSSGP_PAGING_CS.paging_Field4 := p4; + if (use_sig == false) { + SGSN_PTP[sgsn_idx].send(pdu_tx); + } else { + SGSN_SIG[sgsn_idx].send(pdu_tx); + } + return pdu_rx; +} + +/* send paging defined by 'p4' on given SGSN-side index (ptp or signaling) and expect one paging to arrive on + * specified PCU index */ +private function f_send_paging_cs_exp_one_bss(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false,integer pcu_idx := 0) +runs on BSSGP_ConnHdlr { + var template (present) PDU_BSSGP exp_rx; + var boolean test_done := false; + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + exp_rx := f_send_paging_cs(p4, sgsn_idx, use_sig); + /* Expect paging to propagate to the one BSS addressed by the BVCI only */ + timer T := 2.0; + T.start; + alt { + [not use_sig and not test_done] PCU_PTP[pcu_idx].receive(exp_rx) { + setverdict(pass); + test_done := true; + repeat; + } + [not use_sig] PCU_SIG[pcu_idx].receive(exp_rx) { + setverdict(fail, "Received paging on SIGNALING BVC, expected PTP BVC"); + } + [use_sig and not test_done] PCU_SIG[pcu_idx].receive(exp_rx) { + setverdict(pass); + test_done := true; + repeat; + } + [use_sig] PCU_PTP[pcu_idx].receive(exp_rx) { + setverdict(fail, "Received paging on PTP BVC, expected SIGNALING BVC"); + } + [] any from PCU_PTP.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_SIG.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_PTP.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_CS:=?}) { + setverdict(fail, "Different Paging than expected received PTP BVC"); + } + [] any from PCU_SIG.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_CS:=?}) { + setverdict(fail, "Different Paging than expected on SIGNALING BVC"); + } + [not test_done] T.timeout { + setverdict(fail, "Timeout while waiting for paging") + } + [test_done] T.timeout; + } +} + +/* send a CS-PAGING but don't expect it to show up on any PTP or SIG BVC */ +private function f_send_paging_cs_exp_no_bss(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + boolean use_sig := false) +runs on BSSGP_ConnHdlr { + var template (present) PDU_BSSGP exp_rx; + exp_rx := f_send_paging_cs(p4, sgsn_idx, use_sig); + /* Expect paging to propagate to no BSS */ + timer T := 2.0; + T.start; + alt { + [] any from PCU_PTP.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_SIG.receive(exp_rx) { + setverdict(fail, "Paging received on unexpected BVC"); + } + [] any from PCU_PTP.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_CS:=?}) { + setverdict(fail, "Different Paging received on PTP BVC"); + } + [] any from PCU_SIG.receive(PDU_BSSGP:{pDU_BSSGP_PAGING_CS:=?}) { + setverdict(fail, "Different Paging received on SIGNALING BVC"); + } + [] T.timeout { + setverdict(pass); + } + } +} + +private function f_TC_paging_cs_ptp_bss(charstring id) runs on BSSGP_ConnHdlr +{ + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4BssArea, 0, false, 0); +} +testcase TC_paging_cs_ptp_bss() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_bss), testcasename(), 17); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for Location Area */ +private function f_TC_paging_cs_ptp_lac(charstring id) runs on BSSGP_ConnHdlr +{ + var template (present) PDU_BSSGP exp_rx; + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4LAC(pcu_bvc_cfg[0].cell_id.ra_id.lai), 0, false, 0); +} +testcase TC_paging_cs_ptp_lac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_lac), testcasename(), 18); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for unknown Location Area */ +private function f_TC_paging_cs_ptp_lac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var GSM_Types.LocationAreaIdentification unknown_la := { + mcc_mnc := '567F99'H, + lac := 33333 + }; + /* as it's sent on the PTP BVC, we expect it to pass even for unknown LAC */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4LAC(unknown_la), 0, false, 0); +} +testcase TC_paging_cs_ptp_lac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_lac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for Routeing Area */ +private function f_TC_paging_cs_ptp_rac(charstring id) runs on BSSGP_ConnHdlr +{ + /* doesn't really make sense: Sending to a single BVCI means the message ends up + * at that BVC (cell) only, and paging all over the BSS area is not possible */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4RAC(pcu_bvc_cfg[0].cell_id.ra_id), 0, false, 0); +} +testcase TC_paging_cs_ptp_rac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_rac), testcasename(), 19); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for unknown Routeing Area */ +private function f_TC_paging_cs_ptp_rac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var RoutingAreaIdentification unknown_ra := { + lai := { + mcc_mnc := '567F99'H, + lac := 33333 + }, + rac := 254 + }; + /* as it's sent on the PTP BVC, we expect it to pass even for unknown RAC */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4RAC(unknown_ra), 0, false, 0); +} +testcase TC_paging_cs_ptp_rac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_rac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for BVCI (one cell) */ +private function f_TC_paging_cs_ptp_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + /* this should be the normal case for MS in READY MM state after a lower layer failure */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), 0, false, 0); +} +testcase TC_paging_cs_ptp_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_bvci), testcasename(), 20); + f_cleanup(); +} + +/* CS-PAGING on PTP-BVC for unknown BVCI */ +private function f_TC_paging_cs_ptp_bvci_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + /* as it's sent on the PTP BVC, we expect it to pass even for unknown BVCI */ + f_send_paging_cs_exp_one_bss(ts_BssgpP4Bvci(33333), 0, false, 0); +} +testcase TC_paging_cs_ptp_bvci_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_ptp_bvci_unknown), testcasename(), 11); + f_cleanup(); +} + +/* send CS-PAGING on SIG BVC, expect it to arrive on given list of PCU indexes */ +private function f_send_paging_cs_exp_multi(template (value) Paging_Field4 p4, integer sgsn_idx := 0, + ro_integer exp_on_pcu_idx) runs on BSSGP_ConnHdlr +{ + var template (present) PDU_BSSGP exp_rx; + exp_rx := f_send_paging_cs(p4, 0, true); + + /* FIXME: make sure the relevant BVCs/BSS are connected to the ports! */ + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + var default d := activate(as_paging_sig_pcu(i, exp_rx, g_roi)); + defaults := defaults & { d }; + } + f_sleep(2.0); + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + log("Paging received on PCU ", g_roi); + + for (var integer i := 0; i < lengthof(mp_nsconfig_pcu); i := i+1) { + var boolean rx_on_i := ro_integer_contains(g_roi, i); + var boolean exp_on_i := ro_integer_contains(exp_on_pcu_idx, i); + if (exp_on_i and not rx_on_i) { + setverdict(fail, "PS-PAGING not received on ", mp_nsconfig_pcu[i].nsei); + } + if (not exp_on_i and rx_on_i) { + setverdict(fail, "PS-PAGING not expected but received on ", mp_nsconfig_pcu[i].nsei); + } + } + setverdict(pass); +} + +/* CS-PAGING on SIG-BVC for BSS Area */ +private function f_TC_paging_cs_sig_bss(charstring id) runs on BSSGP_ConnHdlr +{ + /* we expect the paging to arrive on all three NSE */ + f_send_paging_cs_exp_multi(ts_BssgpP4BssArea, 0, {0, 1, 2}); +} +testcase TC_paging_cs_sig_bss() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_bss), testcasename(), 13); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for Location Area */ +private function f_TC_paging_cs_sig_lac(charstring id) runs on BSSGP_ConnHdlr +{ + /* The first LAC (13135) is shared by all three NSEs */ + f_send_paging_cs_exp_multi(ts_BssgpP4LAC(pcu_bvc_cfg[0].cell_id.ra_id.lai), 0, {0, 1, 2}); + /* Reset state */ + g_roi := {}; + /* Make LAC (13300) available on pcu index 2 */ + f_connect_to_pcu_bvc(port_idx := 2, nse_idx := 2, bvc_idx := 1); + f_send_paging_cs_exp_multi(ts_BssgpP4LAC(pcu_bvc_cfg[2].cell_id.ra_id.lai), 0, {2}); +} +testcase TC_paging_cs_sig_lac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_lac), testcasename(), 14); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for unknown Location Area */ +private function f_TC_paging_cs_sig_lac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var GSM_Types.LocationAreaIdentification unknown_la := { + mcc_mnc := '567F99'H, + lac := 33333 + }; + f_send_paging_cs_exp_no_bss(ts_BssgpP4LAC(unknown_la), 0, true); +} +testcase TC_paging_cs_sig_lac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_lac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for Routeing Area */ +private function f_TC_paging_cs_sig_rac(charstring id) runs on BSSGP_ConnHdlr +{ + /* Only PCU index 0 has a matching BVC with the RA ID */ + f_send_paging_cs_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[0].cell_id.ra_id), 0, {0}); + g_roi := {}; + /* PCU index 1 and 2 have a matching BVC with the RA ID */ + f_send_paging_cs_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[2].cell_id.ra_id), 0, {1, 2}); + g_roi := {}; + /* PCU index 2 has two matching BVCs with the RA ID */ + f_connect_to_pcu_bvc(port_idx := 2, nse_idx := 2, bvc_idx := 1); + f_send_paging_cs_exp_multi(ts_BssgpP4RAC(pcu_bvc_cfg[2].cell_id.ra_id), 0, {2}); +} +testcase TC_paging_cs_sig_rac() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_rac), testcasename(), 15); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for unknown Routeing Area */ +private function f_TC_paging_cs_sig_rac_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + var RoutingAreaIdentification unknown_ra := { + lai := { + mcc_mnc := '567F99'H, + lac := 33333 + }, + rac := 254 + }; + f_send_paging_cs_exp_no_bss(ts_BssgpP4RAC(unknown_ra), 0, true); +} +testcase TC_paging_cs_sig_rac_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_rac_unknown), testcasename(), 11); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for BVCI (one cell) */ +private function f_TC_paging_cs_sig_bvci(charstring id) runs on BSSGP_ConnHdlr +{ + f_send_paging_cs_exp_multi(ts_BssgpP4Bvci(pcu_bvc_cfg[0].bvci), 0, {0}); +} +testcase TC_paging_cs_sig_bvci() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_bvci), testcasename(), 16); + f_cleanup(); +} + +/* CS-PAGING on SIG-BVC for unknown BVCI */ +private function f_TC_paging_cs_sig_bvci_unknown(charstring id) runs on BSSGP_ConnHdlr +{ + f_send_paging_cs_exp_no_bss(ts_BssgpP4Bvci(33333), 0, true); +} +testcase TC_paging_cs_sig_bvci_unknown() runs on test_CT { + f_init(); + f_start_handlers(refers(f_TC_paging_cs_sig_bvci_unknown), testcasename(), 11); + f_cleanup(); +} + +/*********************************************************************** + * FLUSH-LL procedure + ***********************************************************************/ + +private function f_TC_flush_ll_bvci_new(charstring id) runs on BSSGP_ConnHdlr { + var BssgpBvci bvci := g_pars.pcu[0].cfg.bvc[0].bvci; + var integer i; + for (i := 0; i < 10; i := i+1) { + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_FLUSH_LL(g_pars.tlli, bvci, bvci_new := bvci); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_FLUSH_LL(g_pars.tlli, bvci, bvci_new := bvci); + + f_sgsn2pcu(pdu_tx, pdu_rx, use_sig := true); + + pdu_tx := ts_BSSGP_FLUSH_LL_ACK(g_pars.tlli, int2oct(1, 1), 23, bvci_new := bvci); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_FLUSH_LL_ACK(g_pars.tlli, int2oct(1, 1), 23, bvci_new := bvci); + + f_pcu2sgsn(pdu_tx, pdu_rx, use_sig := true); + } + setverdict(pass); +} + +testcase TC_flush_ll_bvci_new() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_flush_ll_bvci_new), testcasename(), 6); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +private function f_TC_flush_ll_no_bvci_new(charstring id) runs on BSSGP_ConnHdlr { + var BssgpBvci bvci := g_pars.pcu[0].cfg.bvc[0].bvci; + var integer i; + for (i := 0; i < 10; i := i+1) { + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_FLUSH_LL(g_pars.tlli, bvci); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_FLUSH_LL(g_pars.tlli, bvci); + + f_sgsn2pcu(pdu_tx, pdu_rx, use_sig := true); + + pdu_tx := ts_BSSGP_FLUSH_LL_ACK(g_pars.tlli, int2oct(0, 1), 23); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + pdu_rx := tr_BSSGP_FLUSH_LL_ACK(g_pars.tlli, int2oct(0, 1), 23); + + f_pcu2sgsn(pdu_tx, pdu_rx, use_sig := true); + } + setverdict(pass); +} + +testcase TC_flush_ll_no_bvci_new() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_flush_ll_no_bvci_new), testcasename(), 6); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +/*********************************************************************** + * SGSN-INVOKE-TRACE procedure + ***********************************************************************/ + +private altstep as_bssgp_g_pcu_count(integer pcu_idx, template (present) PDU_BSSGP exp_rx, inout ro_integer roi) +runs on GlobalTest_CT { +[] G_PCU[pcu_idx].receive(exp_rx) from g_pcu[pcu_idx].vc_BSSGP { + if (ro_integer_contains(roi, pcu_idx)) { + setverdict(fail, "Received multiple on same SIG BVC"); + } + roi := roi & { pcu_idx }; + repeat; + } +} +/* send a INVOKE-TRACE from SGSN and expect to receive a copy on each NSE */ +testcase TC_trace() runs on GlobalTest_CT +{ + var BSSGP_ConnHdlr vc_conn; + f_init(); + f_global_init(); + + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_INVOKE_TRACE('23'O, '4321'O); + var template (present) PDU_BSSGP exp_rx := ts_BSSGP_INVOKE_TRACE('23'O, '4321'O); + + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + activate(as_bssgp_g_pcu_count(i, exp_rx, g_roi)); + } + G_SGSN[0].send(pdu_tx); + f_sleep(2.0); + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + if (not ro_integer_contains(g_roi, i)) { + setverdict(fail, "Failed to receive TRACE on PCU index ", i); + } + } + setverdict(pass); + + f_cleanup(); +} + +/*********************************************************************** + * LLC-DISCARDED procedure + ***********************************************************************/ + +private function f_TC_llc_discarded(charstring id) runs on BSSGP_ConnHdlr { + var BssgpBvci bvci := g_pars.pcu[0].cfg.bvc[0].bvci; + + var template (value) PDU_BSSGP pdu_tx := ts_BSSGP_LLC_DISCARDED(g_pars.tlli, 23, bvci, 2342); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BSSGP_LLC_DISCARDED(g_pars.tlli, 23, bvci, 2342); + + f_pcu2sgsn(pdu_tx, pdu_rx, use_sig := true); + + setverdict(pass); +} +/* Send a LLC-DISCARDED from BSS side and expect it to show up on SGSN (SIG BVC) */ +testcase TC_llc_discarded() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_llc_discarded), testcasename(), 6); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +/*********************************************************************** + * OVERLOAD procedure + ***********************************************************************/ + +/* Send an OVERLOAD from SGSN side and expect it to show up on each PCU (SIG BVC) */ +testcase TC_overload() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + + var template (value) PDU_BSSGP pdu_tx := ts_OVERLOAD('1'B); + var template (present) PDU_BSSGP exp_rx := tr_OVERLOAD('1'B); + + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + activate(as_bssgp_g_pcu_count(i, exp_rx, g_roi)); + } + G_SGSN[0].send(pdu_tx); + f_sleep(2.0); + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + if (not ro_integer_contains(g_roi, i)) { + setverdict(fail, "Failed to receive OVERLOAD on PCU index ", i); + } + } + setverdict(pass); + + f_cleanup(); +} + +/*********************************************************************** + * BVC-BLOCK / BVC-UNBLOCK procedure + ***********************************************************************/ + +private function f_block_ptp_bvc_from_pcu(integer pcu_idx, integer bvc_idx) runs on test_CT +{ + var BSSGP_BVC_CT bvc_ct := g_pcu[pcu_idx].vc_BSSGP_BVC[bvc_idx]; + var BssgpBvcConfig bvc_cfg := g_pcu[pcu_idx].cfg.bvc[bvc_idx]; + var Nsei nsei_pcu := g_pcu[pcu_idx].cfg.nsei; + + SGSN_MGMT.clear; + PCU_MGMT.clear; + + /* block the PTP BVC from the PCU side */ + PCU_MGMT.send(BssgpBlockRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to bvc_ct; + /* expect state on both PCU and SGSN side to change */ + interleave { + [] PCU_MGMT.receive(tr_BssgpStsInd(nsei_pcu, bvc_cfg.bvci, BVC_S_BLOCKED)) from bvc_ct; + [] SGSN_MGMT.receive(tr_BssgpStsInd(mp_nsconfig_sgsn[0].nsei, bvc_cfg.bvci, BVC_S_BLOCKED)); + [] SGSN_MGMT.receive(tr_BssgpStsInd(mp_nsconfig_sgsn[1].nsei, bvc_cfg.bvci, BVC_S_BLOCKED)); + /* Doesn't auto-scale with NUM_SGSN */ + } + setverdict(pass); +} +testcase TC_bvc_block_ptp() runs on test_CT +{ + f_init(); + f_sleep(1.0); + f_block_ptp_bvc_from_pcu(0, 0); + f_cleanup(); +} + +private function f_unblock_ptp_bvc_from_pcu(integer pcu_idx, integer bvc_idx) runs on test_CT +{ + var BSSGP_BVC_CT bvc_ct := g_pcu[pcu_idx].vc_BSSGP_BVC[bvc_idx]; + var BssgpBvcConfig bvc_cfg := g_pcu[pcu_idx].cfg.bvc[bvc_idx]; + var Nsei nsei_pcu := g_pcu[pcu_idx].cfg.nsei; + + SGSN_MGMT.clear; + PCU_MGMT.clear; + + /* block the PTP BVC from the PCU side */ + PCU_MGMT.send(BssgpUnblockRequest:{}) to bvc_ct; + /* expect state on both PCU and SGSN side to change */ + interleave { + [] PCU_MGMT.receive(tr_BssgpStsInd(nsei_pcu, bvc_cfg.bvci, BVC_S_UNBLOCKED)) from bvc_ct; + [] SGSN_MGMT.receive(tr_BssgpStsInd(mp_nsconfig_sgsn[0].nsei, bvc_cfg.bvci, BVC_S_UNBLOCKED)); + [] SGSN_MGMT.receive(tr_BssgpStsInd(mp_nsconfig_sgsn[1].nsei, bvc_cfg.bvci, BVC_S_UNBLOCKED)); + /* Doesn't auto-scale with NUM_SGSN */ + } + setverdict(pass); +} +testcase TC_bvc_unblock_ptp() runs on test_CT +{ + f_init(); + f_sleep(1.0); + f_block_ptp_bvc_from_pcu(0, 0); + f_sleep(1.0); + f_unblock_ptp_bvc_from_pcu(0, 0); + f_cleanup(); +} + +/*********************************************************************** + * BVC-RESET procedure + ***********************************************************************/ +private altstep as_count_bvc_reset(integer sgsn_idx, BssgpBvci bvci, inout roro_integer roroi) +runs on test_CT { + var BSSGP_BVC_CT sgsn_bvc_ct := f_get_sgsn_bvc_ct(sgsn_idx, bvci); + [] SGSN_MGMT.receive(BssgpResetIndication:{bvci}) from sgsn_bvc_ct { + roroi[sgsn_idx] := roroi[sgsn_idx] & { bvci }; + repeat; + } +} +private altstep as_ignore_status(BSSGP_BVC_MGMT_PT pt) { +[] pt.receive(BssgpStatusIndication:?) { repeat; } +} +private function f_get_sgsn_bvc_ct(integer sgsn_idx, BssgpBvci bvci) runs on test_CT return BSSGP_BVC_CT { + for (var integer i := 0; i < lengthof(g_sgsn[sgsn_idx].cfg.bvc); i := i+1) { + if (g_sgsn[sgsn_idx].cfg.bvc[i].bvci == bvci) { + return g_sgsn[sgsn_idx].vc_BSSGP_BVC[i]; + } + } + return null; +} +private function f_reset_ptp_bvc_from_pcu(integer pcu_idx, integer bvc_idx) runs on test_CT +{ + var BSSGP_BVC_CT pcu_bvc_ct := g_pcu[pcu_idx].vc_BSSGP_BVC[bvc_idx]; + var BssgpBvcConfig bvc_cfg := g_pcu[pcu_idx].cfg.bvc[bvc_idx]; + var Nsei nsei_pcu := g_pcu[pcu_idx].cfg.nsei; + var ro_default defaults; + var integer i; + + SGSN_MGMT.clear; + PCU_MGMT.clear; + + for (i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + g_roroi[i] := {}; + } + + /* block the PTP BVC from the PCU side */ + PCU_MGMT.send(BssgpResetRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to pcu_bvc_ct; + + /* expect state on both PCU and SGSN side to change */ + defaults := { activate(as_ignore_status(SGSN_MGMT)) }; + + /* Activate altsteps: One for each SGSN */ + for (i := 0; i < lengthof(g_sgsn); i := i+1) { + var default d := activate(as_count_bvc_reset(i, bvc_cfg.bvci, g_roroi)); + defaults := defaults & { d }; + } + + timer T := 3.0; + T.start; + alt { + [] PCU_MGMT.receive(tr_BssgpStsInd(nsei_pcu, bvc_cfg.bvci, BVC_S_BLOCKED)) from pcu_bvc_ct { + g_roi := g_roi & { bvc_cfg.bvci }; + repeat; + } + [] T.timeout; + } + + for (i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + /* Check if BVC-RESET was received at all SGSNs */ + for (i := 0; i < lengthof(g_sgsn); i := i+1) { + if (not ro_integer_contains(g_roroi[i], bvc_cfg.bvci)) { + setverdict(fail, "Missing SGSN[", i, "] BVC-BLOCK of BVCI=", bvc_cfg.bvci); + } + } + + setverdict(pass); + f_cleanup(); +} +/* Send a BVC-RESET for a PTP BVC from the BSS side: expect it to propagate */ +testcase TC_bvc_reset_ptp_from_bss() runs on test_CT +{ + f_init(); + f_sleep(3.0); + f_reset_ptp_bvc_from_pcu(0, 0); + f_cleanup(); +} + +private altstep as_count_bvc_sts(integer sgsn_idx, BssgpBvci bvci, + template (present) BvcState exp_bvc_state, inout roro_integer roroi) +runs on test_CT { + var BSSGP_BVC_CT sgsn_bvc_ct := f_get_sgsn_bvc_ct(sgsn_idx, bvci); + [] SGSN_MGMT.receive(tr_BssgpStsInd(?, bvci, exp_bvc_state)) from sgsn_bvc_ct { + roroi[sgsn_idx] := roroi[sgsn_idx] & { bvci }; + repeat; + } +} + +private altstep as_count_bvc_block(integer sgsn_idx, BssgpBvci bvci, inout roro_integer roroi) +runs on test_CT { + [] as_count_bvc_sts(sgsn_idx, bvci, BVC_S_BLOCKED, roroi); +} + +private altstep as_count_bvc_unblock(integer sgsn_idx, BssgpBvci bvci, inout roro_integer roroi) +runs on test_CT { + [] as_count_bvc_sts(sgsn_idx, bvci, BVC_S_UNBLOCKED, roroi); +} + +/* reset the signaling BVC from one BSS; expect no signaling BVC reset on SGSN; but BVC-BLOCK for PTP */ +testcase TC_bvc_reset_sig_from_bss() runs on test_CT { + + f_init(); + f_sleep(3.0); + + for (var integer i := 0; i < lengthof(mp_nsconfig_sgsn); i := i+1) { + g_roroi[i] := {}; + } + + /* Start BVC-RESET procedure for BVCI=0 */ + PCU_MGMT.send(BssgpResetRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to g_pcu[0].vc_BSSGP; + + /* Activate altsteps: One for each PTP BVC and SGSN within that PCUs NSE */ + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(g_pcu[0].cfg.bvc); i := i+1) { + var BssgpBvcConfig bvcc := g_pcu[0].cfg.bvc[i]; + for (var integer j := 0; j < lengthof(g_sgsn); j := j+1) { + var default d := activate(as_count_bvc_block(j, bvcc.bvci, g_roroi)); + defaults := defaults & { d }; + } + } + + timer T := 3.0; T.start; alt { - [] PCU_SIG[ran_idx].receive(tr_BSSGP_RESUME_ACK(g_pars.tlli, g_pars.bssgp_cell_id[ran_idx].ra_id)); - [] PCU_SIG[ran_idx].receive(tr_BSSGP_RESUME_NACK(g_pars.tlli, g_pars.bssgp_cell_id[ran_idx].ra_id, -?)) { - setverdict(fail, "RESUME-NACK in response to RESUME for TLLI ", g_pars.tlli); - mtc.stop; + [] SGSN_MGMT.receive(BssgpResetIndication:{0}) { + setverdict(fail, "BSS-side Reset of BVCI=0 should not propagate"); + } + [] T.timeout; + } + + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + /* check if BVC-block was received on all expected BVC/SGSN */ + for (var integer i := 0; i < lengthof(g_pcu[0].cfg.bvc); i := i+1) { + var BssgpBvcConfig bvcc := g_pcu[0].cfg.bvc[i]; + for (var integer j := 0; j < lengthof(g_sgsn); j := j+1) { + if (not ro_integer_contains(g_roroi[j], bvcc.bvci)) { + setverdict(fail, "Missing SGSN[", j, "] BVC-BLOCK of BVCI=", bvcc.bvci); + } + } + } + + /* check if BVC-block was not received on any unexpected BVC is not required as + * such a message would basically run into 'no matching clause' */ + setverdict(pass); + f_cleanup(); +} + +private function f_reset_ptp_bvc_from_sgsn(integer pcu_idx, integer bvc_idx) runs on test_CT +{ + var BSSGP_BVC_CT pcu_bvc_ct := g_pcu[pcu_idx].vc_BSSGP_BVC[bvc_idx]; + var BssgpBvcConfig bvc_cfg := g_pcu[pcu_idx].cfg.bvc[bvc_idx]; + var Nsei nsei_pcu := g_pcu[pcu_idx].cfg.nsei; + var BSSGP_BVC_CT sgsn_bvc_ct := f_get_sgsn_bvc_ct(0, bvc_cfg.bvci); + var default d; + + SGSN_MGMT.clear; + PCU_MGMT.clear; + + /* block the PTP BVC from the PCU side */ + SGSN_MGMT.send(BssgpResetRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to sgsn_bvc_ct; + /* expect state on both PCU and SGSN side to change */ + d := activate(as_ignore_status(PCU_MGMT)); + interleave { + [] SGSN_MGMT.receive(tr_BssgpStsInd(?, bvc_cfg.bvci, BVC_S_BLOCKED)) from sgsn_bvc_ct; + [] PCU_MGMT.receive(BssgpResetIndication:{bvc_cfg.bvci}) from pcu_bvc_ct; + } + deactivate(d); + setverdict(pass); +} + +/* Send a BVC-RESET for a PTP BVC from the SGSN side: expect it to propagate */ +testcase TC_bvc_reset_ptp_from_sgsn() runs on test_CT +{ + f_init(); + f_sleep(3.0); + f_reset_ptp_bvc_from_sgsn(0, 0); + f_cleanup(); +} + +/* Send a BVC-RESET for a blocked PTP BVC from the SGSN side: expect NS-STATUS with cause BVCI unknown */ +testcase TC_bvc_reset_blocked_ptp_from_sgsn() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_sleep(3.0); + /* Make sure NS for BVC is down and try again */ + f_disable_ns_pcu(0); + f_sleep(10.0); + + var BssgpBvcConfig bvc_cfg := g_pcu[0].cfg.bvc[0]; + SGSN_MGMT.send(BssgpResetRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to f_get_sgsn_bvc_ct(0, bvc_cfg.bvci); + + /* Check for NS-STATUS with BVCI unknown, ignore other messages */ + var template (present) PDU_BSSGP exp_rx := + tr_BSSGP_STATUS(bvc_cfg.bvci, BSSGP_CAUSE_BVCI_UNKNOWN, ?); + + alt { + [] G_SGSN[0].receive(exp_rx) { + setverdict(pass); + } + [] SGSN_MGMT.receive(BssgpStatusIndication:{*, bvc_cfg.bvci, BVC_S_UNBLOCKED}) { + setverdict(fail, "BVC unblocked that should be gone on BSS side"); + } + [] SGSN_MGMT.receive { + repeat; + } + [] G_SGSN[0].receive { + repeat; + } + } + + f_cleanup(); +} + +private altstep as_ignore_mgmt(BSSGP_BVC_MGMT_PT pt) { + [] pt.receive {repeat; } +} + +private altstep as_count_bvc0_block(integer pcu_idx, Nsei nsei, inout ro_integer roi) +runs on test_CT { + var BSSGP_CT pcu_ct := g_pcu[pcu_idx].vc_BSSGP; + [] PCU_MGMT.receive(BssgpResetIndication:{0}) from pcu_ct { + roi := roi & { nsei }; + repeat; + } +} + +/* reset the signaling BVC from the SGSN; expect all signaling BVC on all BSS to be reset */ +testcase TC_bvc_reset_sig_from_sgsn() runs on test_CT { + + f_init(); + f_sleep(3.0); + + SGSN_MGMT.clear; + PCU_MGMT.clear; + + /* Start BVC-RESET procedure for BVCI=0 */ + SGSN_MGMT.send(BssgpResetRequest:{cause:=BSSGP_CAUSE_OM_INTERVENTION}) to g_sgsn[0].vc_BSSGP; + + /* Defaults match in reverse activation order, this one is a catch-all for Status indications + * and reset indications sent from other components (like the ptp_bvcs). If we don't drain + * the port and a different message sits at the front we wait forever and fail the test. + */ + var ro_default defaults := { activate(as_ignore_mgmt(PCU_MGMT)) }; + + /* Activate altsteps: One for each PCU NSE */ + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + var NSConfiguration nscfg := mp_nsconfig_pcu[i]; + var default d := activate(as_count_bvc0_block(i, nscfg.nsei, g_roi)); + defaults := defaults & { d }; + } + + f_sleep(3.0); + + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + /* check if BVC-block was received on all expected BVC */ + for (var integer i := 0; i < lengthof(g_pcu); i := i+1) { + var NSConfiguration nscfg := mp_nsconfig_pcu[i]; + if (not ro_integer_contains(g_roi, nscfg.nsei)) { + setverdict(fail, "Missing PCU-side BVC-RESET of BVCI=0 on PCU index ", i); + } + } + + /* check if BVC-block was not received on any unexpected BVC is not required as + * such a message would basically run into 'no matching clause' */ + + f_cleanup(); +} + +/*********************************************************************** + * FLOW-CONTROL-BVC procedure + ***********************************************************************/ + +private altstep as_g_count_sgsn(integer sgsn_idx, inout ro_integer roi, + template PDU_BSSGP exp_rx, template (omit) PDU_BSSGP tx_reply) +runs on GlobalTest_CT { + [] G_SGSN[sgsn_idx].receive(exp_rx) { + roi := roi & { sgsn_idx }; + if (ispresent(tx_reply)) { + G_SGSN[sgsn_idx].send(tx_reply); + } + repeat; + } +} +/* Send FC-BVC from simulated PCU; expect each SGSN to receive it; expect PCU to receive ACK */ +testcase TC_fc_bvc() runs on GlobalTest_CT +{ + f_init(); + f_global_init_ptp(); + + var template (value) PDU_BSSGP pdu_tx := ts_BVC_FC_BVC(10240, 2000, 1024, 1000, '01'O); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP pdu_rx := tr_BVC_FC_BVC(10240, 2000, 1024, 1000, '01'O); + var template (value) PDU_BSSGP ack_tx := + ts_BVC_FC_BVC_ACK(pdu_tx.pDU_BSSGP_FLOW_CONTROL_BVC.tag.unstructured_Value); + + /* Send a FC-BVC from BSS to gbproxy, expect an ACK in response */ + G_PCU[0].send(pdu_tx); + + /* Activate altsteps: One for each SGSN-side PTP BVC port */ + var ro_default defaults := {}; + for (var integer i := 0; i < lengthof(g_sgsn); i := i+1) { + var default d := activate(as_g_count_sgsn(i, g_roi, pdu_rx, ack_tx)); + defaults := defaults & { d }; + } + + f_sleep(3.0); + + for (var integer i := 0; i < lengthof(defaults); i := i+1) { + deactivate(defaults[i]); + } + + /* check if BVC-block was received on all expected BVC */ + for (var integer i := 0; i < lengthof(g_sgsn); i := i+1) { + if (not ro_integer_contains(g_roi, i)) { + setverdict(fail, "Missing BVC-FLOW-CONTROL on SGSN index ", i); + } + } + + /* Expect ACK on PCU side */ + G_PCU[0].receive(ack_tx); + + setverdict(pass); + + f_cleanup(); +} + +/*********************************************************************** + * FLOW-CONTROL-MS procedure + ***********************************************************************/ + +private function f_TC_fc_ms(charstring id) runs on BSSGP_ConnHdlr { + var BssgpBvci bvci := g_pars.pcu[0].cfg.bvc[0].bvci; + + var template (value) PDU_BSSGP fc_tx := ts_BVC_FC_MS(g_pars.tlli, 100, 200, '12'O); + /* we cannot use pdu_tx as there are some subtle differences in the length field :/ */ + var template (present) PDU_BSSGP fc_rx := tr_BVC_FC_MS(g_pars.tlli, 100, 200, '12'O); + var template (value) PDU_BSSGP ack_tx := ts_BVC_FC_MS_ACK(g_pars.tlli, '12'O); + + f_pcu2sgsn(fc_tx, fc_rx, use_sig := false); + f_sgsn2pcu(ack_tx, ack_tx, use_sig := false); + + setverdict(pass); +} +/* Send a FLOW-CONTROL-MS from BSS side and expect it to show up on SGSN (PTP BVC) */ +testcase TC_fc_ms() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_fc_ms), testcasename(), 21); + /* TODO: start multiple handlers (UEs) on various cells on same and other NSEs */ + f_cleanup(); +} + +/*********************************************************************** + * MS-REGISTRATION ENQUIRY procedure + ***********************************************************************/ + +private function f_TC_ms_reg_enq(charstring id) runs on BSSGP_ConnHdlr +{ + f_pcu2any_sgsn(ts_BSSGP_MS_REG_ENQ(g_pars.imsi), tr_BSSGP_MS_REG_ENQ(g_pars.imsi), use_sig := true); + f_sgsn2pcu(ts_BSSGP_MS_REW_ENQ_RESP(g_pars.imsi, omit), tr_BSSGP_MS_REW_ENQ_RESP(g_pars.imsi, omit), use_sig := true); +} +testcase TC_ms_reg_enq() runs on test_CT +{ + f_init(); + f_start_handlers(refers(f_TC_ms_reg_enq), testcasename(), 22); + f_cleanup(); +} + +/*********************************************************************** + * RIM (RAN Information Management) + ***********************************************************************/ + +/* Our tests here are rather synthetic, as they don't reflect normal message flows + as they would be observed in a live network. However, for testing gbproxy, this shouldn't + matter as gbproxy is not concerned with anything but the source / destination routing + information */ + +/* gbproxy must route all unknown RIM Routing Info (Cell Id) to the SGSN. We just define + one here of which we know it is not used among the [simulated] PCUs */ +const BssgpCellId cell_id_sgsn := { + ra_id := { + lai := { + mcc_mnc := c_mcc_mnc, + lac := 65534 + }, + rac := 0 + }, + cell_id := 65533 +}; + +/* Send 'tx' on PTP-BVCI from PCU; expect 'rx' on any of our SGSN (RIM can be routed anywhere) */ +friend function f_rim_pcu2sgsn(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx, + integer pcu_idx := 0) runs on GlobalTest_CT { + var PDU_BSSGP rx; + timer T := 2.0; + + RIM_PCU[pcu_idx].send(tx); + T.start; + alt { + [] any from RIM_SGSN.receive(exp_rx) { + setverdict(pass); + } + [] any from RIM_SGSN.receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on SGSN side: ", rx)); } [] T.timeout { - setverdict(fail, "No RESUME-ACK in response to SUSPEND for TLLI ", g_pars.tlli); - mtc.stop; + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on SGSN side: ", exp_rx)); } } } +/* Send 'tx' on PTP-BVCI from SGSN; expect 'rx' on PCU */ +friend function f_rim_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 := 2.0; + + RIM_SGSN[sgsn_idx].send(tx); + T.start; + alt { + [] RIM_PCU[pcu_idx].receive(exp_rx) { + setverdict(pass); + } + [] RIM_PCU[pcu_idx].receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on PCU side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on PCU side: ", exp_rx)); + } + } +} + +/* Send 'tx' on PTP-BVCI from SRC-PCU; expect 'rx' on DST-PCU */ +friend function f_rim_pcu2pcu(template (value) PDU_BSSGP tx, template (present) PDU_BSSGP exp_rx, + integer src_pcu_idx, integer dst_pcu_idx) runs on GlobalTest_CT { + var integer rx_idx; + var PDU_BSSGP rx; + timer T := 2.0; + + RIM_PCU[src_pcu_idx].send(tx); + T.start; + alt { + [] RIM_PCU[dst_pcu_idx].receive(exp_rx) -> value rx{ + setverdict(pass); + } + [] any from RIM_PCU.receive(exp_rx) -> @index value rx_idx { + setverdict(fail, "Received RIM on wrong PCU[", rx_idx ,"], expected on PCU[", dst_pcu_idx, "]"); + } + [] any from RIM_SGSN.receive(exp_rx) { + setverdict(fail, "Received RIM on SGSN but expected it on other PCU"); + } + [] any from RIM_SGSN.receive(PDU_BSSGP:?) -> value rx { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected BSSGP on SGSN side: ", rx)); + } + [] T.timeout { + f_shutdown(__FILE__, __LINE__, fail, + log2str("Timeout waiting for BSSGP on SGSN side: ", exp_rx)); + } + } +} + + +type function rim_fn(integer sgsn_idx, integer pcu_idx, integer bvc_idx) runs on GlobalTest_CT; + +/* helper function for the RIM test cases: Execute 'fn' for each BVC on each PCU for + each SGSN */ +private function f_rim_iterator(rim_fn fn) runs on GlobalTest_CT +{ + var integer sgsn_idx, pcu_idx, bvc_idx; + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx+1) { + for (pcu_idx := 0; pcu_idx < lengthof(g_pcu); pcu_idx := pcu_idx+1) { + for (bvc_idx := 0; bvc_idx < lengthof(g_pcu[pcu_idx].cfg.bvc); bvc_idx := bvc_idx+1) { + log("Testing RIM SGSN[", sgsn_idx, "] <-> PCU[", pcu_idx, "][", bvc_idx, "]"); + fn.apply(sgsn_idx, pcu_idx, bvc_idx); + } + } + } +} + +/* RAN-INFORMATION-REQUEST */ +private function f_TC_rim_info_req(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) RAN_Information_Request_RIM_Container cont_tx; + var template RAN_Information_Request_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_cid(cell_id_sgsn); + + cont_tx := ts_RAN_Information_Request_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0), + ts_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + cont_rx := tr_RAN_Information_Request_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0), + tr_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + + f_rim_pcu2sgsn(ts_RAN_INFORMATION_REQUEST(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_RAN_INFORMATION_REQUEST(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); + + f_rim_sgsn2pcu(ts_RAN_INFORMATION_REQUEST(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_tx), + tr_RAN_INFORMATION_REQUEST(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); +} +testcase TC_rim_info_req() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_info_req)); + f_cleanup(); +} + +/* RAN-INFORMATION */ +private function f_TC_rim_info(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) RAN_Information_RIM_Container cont_tx; + var template RAN_Information_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_cid(cell_id_sgsn); + + cont_tx := ts_RAN_Information_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0), + ts_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + + cont_rx := tr_RAN_Information_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0), + tr_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + + f_rim_pcu2sgsn(ts_PDU_BSSGP_RAN_INFORMATION(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); + + f_rim_sgsn2pcu(ts_PDU_BSSGP_RAN_INFORMATION(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); +} +testcase TC_rim_info() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_info)); + f_cleanup(); +} + +/* RAN-INFORMATION-ACK */ +private function f_TC_rim_info_ack(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) RAN_Information_Ack_RIM_Container cont_tx; + var template RAN_Information_Ack_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_cid(cell_id_sgsn); + + cont_tx := ts_RAN_Information_Ack_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0)); + + cont_rx := tr_RAN_Information_Ack_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0)); + + f_rim_pcu2sgsn(ts_PDU_BSSGP_RAN_INFORMATION_ACK(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_ACK(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); + + f_rim_sgsn2pcu(ts_PDU_BSSGP_RAN_INFORMATION_ACK(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_ACK(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); +} +testcase TC_rim_info_ack() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_info_ack)); + f_cleanup(); +} + +/* RAN-INFORMATION-ERROR */ +private function f_TC_rim_info_error(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) RAN_Information_Error_RIM_Container cont_tx; + var template RAN_Information_Error_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_cid(cell_id_sgsn); + + cont_tx := ts_RAN_Information_Error_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_BSSGP_CAUSE(BSSGP_CAUSE_EQUIMENT_FAILURE), + omit, valueof(ts_BVC_UNBLOCK(23))); + + cont_rx := tr_RAN_Information_Error_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_BSSGP_CAUSE(BSSGP_CAUSE_EQUIMENT_FAILURE), + omit, enc_PDU_BSSGP(valueof(tr_BVC_UNBLOCK(23)))); + + f_rim_pcu2sgsn(ts_PDU_BSSGP_RAN_INFORMATION_ERROR(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_ERROR(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); + + f_rim_sgsn2pcu(ts_PDU_BSSGP_RAN_INFORMATION_ERROR(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_ERROR(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); +} +testcase TC_rim_info_error() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_info_error)); + f_cleanup(); +} + +////////////////// +/* RAN-INFORMATION-APPLICATION-ERROR */ +private function f_TC_rim_info_app_error(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) Application_Error_Container app_cont_tx; + var template Application_Error_Container app_cont_rx; + var template (value) RAN_Information_Application_Error_RIM_Container cont_tx; + var template RAN_Information_Application_Error_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_cid(cell_id_sgsn); + + app_cont_tx := tsu_Application_Error_Container_NACC(cell_id, 23, + tsu_Application_Container_IE_NACC_req(cell_id)); + + app_cont_rx := rsu_Application_Error_Container_NACC(cell_id, 23, + rsu_Application_Container_IE_NACC_req(cell_id)); + + cont_tx := ts_RAN_Information_Application_Error_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0), + ts_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP), + omit, app_cont_tx); + cont_rx := tr_RAN_Information_Application_Error_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0), + tr_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP), + omit, app_cont_rx); + + f_rim_pcu2sgsn(ts_PDU_BSSGP_RAN_INFORMATION_APPLICATION_ERROR(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_APPLICATION_ERROR(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); + + f_rim_sgsn2pcu(ts_PDU_BSSGP_RAN_INFORMATION_APPLICATION_ERROR(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION_APPLICATION_ERROR(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); +} +testcase TC_rim_info_app_error() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_info_app_error)); + f_cleanup(); +} + +/* RAN-INFORMATION routing directly between PCUs, without SGSN involvement */ +private function f_TC_rim_info_pcu2pcu(integer src_pcu_idx, integer src_bvc_idx, + integer dst_pcu_idx, integer dst_bvc_idx) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id_src := g_pcu[src_pcu_idx].cfg.bvc[src_bvc_idx].cell_id; + var BssgpCellId cell_id_dst := g_pcu[dst_pcu_idx].cfg.bvc[dst_bvc_idx].cell_id; + var template (value) RIM_Routing_Information ri_pcu_src_tx; + var template (value) RIM_Routing_Information ri_pcu_dst_tx; + var template RIM_Routing_Information ri_pcu_src_rx; + var template RIM_Routing_Information ri_pcu_dst_rx; + var template (value) RAN_Information_RIM_Container cont_tx; + var template RAN_Information_RIM_Container cont_rx; + + log("Testing RIM PCU2PCU from PCU[", src_pcu_idx, "][", src_bvc_idx, "] to PCU[", + dst_pcu_idx, "][", dst_bvc_idx, "]"); + + ri_pcu_src_tx := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, + t_RIM_Routing_Address_cid(cell_id_src)); + ri_pcu_dst_tx := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, + t_RIM_Routing_Address_cid(cell_id_dst)); + ri_pcu_src_rx := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, + t_RIM_Routing_Address_cid(cell_id_src)); + ri_pcu_dst_rx := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, + t_RIM_Routing_Address_cid(cell_id_dst)); + + cont_tx := ts_RAN_Information_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0), + ts_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + cont_rx := tr_RAN_Information_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0), + tr_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + + f_rim_pcu2pcu(ts_PDU_BSSGP_RAN_INFORMATION(dst := ri_pcu_dst_tx, src := ri_pcu_src_tx, cont := cont_tx), + tr_PDU_BSSGP_RAN_INFORMATION(dst := ri_pcu_dst_rx, src := ri_pcu_src_rx, cont := cont_rx), + src_pcu_idx, dst_pcu_idx); +} +testcase TC_rim_info_pcu2pcu() runs on GlobalTest_CT +{ + var integer src_pcu_idx, dst_pcu_idx; + var integer src_bvc_idx, dst_bvc_idx; + f_init(); + f_global_init(); + + for (src_pcu_idx := 0; src_pcu_idx < lengthof(g_pcu); src_pcu_idx := src_pcu_idx + 1) { + for (src_bvc_idx := 0; src_bvc_idx < lengthof(g_pcu[src_pcu_idx].cfg.bvc); src_bvc_idx := src_bvc_idx + 1) { + for (dst_pcu_idx := 0; dst_pcu_idx < lengthof(g_pcu); dst_pcu_idx := dst_pcu_idx + 1) { + if (dst_pcu_idx == src_pcu_idx) { + continue; + } + + for (dst_bvc_idx := 0; dst_bvc_idx < lengthof(g_pcu[dst_pcu_idx].cfg.bvc); +dst_bvc_idx := dst_bvc_idx + 1) { + f_TC_rim_info_pcu2pcu(src_pcu_idx, src_bvc_idx, dst_pcu_idx, dst_bvc_idx); + } + } + } + } + + f_cleanup(); +} + + +/* Test RIM REQ sent from an MME->SGSN->GBPROXY->PCU and back (eNACC) */ +private function f_TC_rim_from_eutran(integer sgsn_idx, integer pcu_idx, integer bvc_idx := 0) +runs on GlobalTest_CT +{ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var template (value) RAN_Information_Request_RIM_Container cont_tx; + var template RAN_Information_Request_RIM_Container cont_rx; + var template RIM_Routing_Address ra_pcu; + var template RIM_Routing_Address ra_sgsn; + + ra_pcu := t_RIM_Routing_Address_cid(cell_id); + ra_sgsn := t_RIM_Routing_Address_enbid(cell_id_sgsn, tac := 3, gnbid := '12345678123456'O); + + cont_tx := ts_RAN_Information_Request_RIM_Container(ts_RIM_Application_Identity(RIM_APP_ID_NACC), + ts_RIM_Sequence_Number(0), + ts_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + cont_rx := tr_RAN_Information_Request_RIM_Container(tr_RIM_Application_Identity(RIM_APP_ID_NACC), + tr_RIM_Sequence_Number(0), + tr_RIM_PDU_Indications(false, RIM_PDU_TYPE_STOP)); + + f_rim_sgsn2pcu(ts_RAN_INFORMATION_REQUEST(dst := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := ts_RIM_Routing_Information(RIM_ADDR_EUTRAN_NODEB_ID, ra_sgsn), + cont := cont_tx), + tr_RAN_INFORMATION_REQUEST(dst := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + src := tr_RIM_Routing_Information(RIM_ADDR_EUTRAN_NODEB_ID, ra_sgsn), + cont := cont_rx), + sgsn_idx, pcu_idx); + + + f_rim_pcu2sgsn(ts_RAN_INFORMATION_REQUEST(dst := ts_RIM_Routing_Information(RIM_ADDR_EUTRAN_NODEB_ID, ra_sgsn), + src := ts_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_tx), + tr_RAN_INFORMATION_REQUEST(dst := tr_RIM_Routing_Information(RIM_ADDR_EUTRAN_NODEB_ID, ra_sgsn), + src := tr_RIM_Routing_Information(RIM_ADDR_GERAN_CELL_ID, ra_pcu), + cont := cont_rx), + pcu_idx); +} +testcase TC_rim_from_eutran() runs on GlobalTest_CT +{ + f_init(); + f_global_init(); + f_rim_iterator(refers(f_TC_rim_from_eutran)); + f_cleanup(); +} + +/*********************************************************************** + * STATUS handling + ***********************************************************************/ + +/* BSSGP STATUS PDU must be routed based on inner "PDU In Error" message */ + +/* generate a TMSI with NRI matching sgsn_idx + nri_idx */ +private function f_gen_tmsi_for_sgsn_nri(integer sgsn_idx, integer nri_idx) runs on test_CT return OCT4 +{ + var integer nri := mp_sgsn_nri[sgsn_idx][nri_idx]; + return f_gen_tmsi(0, nri_v := nri, nri_bitlen := mp_nri_bitlength); +} + +/* generate a TLLI with NRI matching sgsn_idx + nri_idx */ +private function f_gen_tlli_for_sgsn_nri(integer sgsn_idx, integer nri_idx) runs on test_CT return OCT4 +{ + var OCT4 p_tmsi := f_gen_tmsi_for_sgsn_nri(sgsn_idx, nri_idx); + return f_gprs_tlli_from_tmsi(p_tmsi, TLLI_LOCAL); +} + +/* STATUS in uplink direction; expect routing by its NRI */ +private function f_TC_status_ul(integer pcu_idx, integer sgsn_idx, PDU_BSSGP inner) +runs on GlobalTest_CT +{ + var template (value) PDU_BSSGP tx := ts_BSSGP_STATUS(omit, BSSGP_CAUSE_EQUIMENT_FAILURE, inner); + var template (present) PDU_BSSGP exp_rx := + tr_BSSGP_STATUS(omit, BSSGP_CAUSE_EQUIMENT_FAILURE, + tx.pDU_BSSGP_STATUS.pDU_in_Error.erroneous_BSSGP_PDU); + + f_global_pcu2sgsn(tx, exp_rx, pcu_idx, sgsn_idx); +} + +/* STATUS in uplink direction; expect routing by its NRI */ +private function f_TC_status_dl(integer sgsn_idx, integer pcu_idx, PDU_BSSGP inner) +runs on GlobalTest_CT +{ + var template (value) PDU_BSSGP tx := ts_BSSGP_STATUS(omit, BSSGP_CAUSE_EQUIMENT_FAILURE, inner); + var template (present) PDU_BSSGP exp_rx := + tr_BSSGP_STATUS(omit, BSSGP_CAUSE_EQUIMENT_FAILURE, + tx.pDU_BSSGP_STATUS.pDU_in_Error.erroneous_BSSGP_PDU); + + f_global_sgsn2pcu(tx, exp_rx, sgsn_idx, pcu_idx); +} + +/* STATUS in uplink direction on SIG-BVC containing a TLLI; expect routing by its NRI */ +testcase TC_status_sig_ul_tlli() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + + f_init(); + f_global_init(); + + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx + 1) { + /* some downlink PDU occurring on SIG-BVC with a TLLI */ + var OCT4 tlli := f_gen_tlli_for_sgsn_nri(sgsn_idx, nri_idx); + var PDU_BSSGP inner := valueof(ts_BSSGP_FLUSH_LL(tlli, 2342)); + + f_TC_status_ul(0, sgsn_idx, inner); + } + } + + f_cleanup(); +} + +/* STATUS in uplink direction on SIG-BVC containing a TMSI; expect routing by its NRI */ +testcase TC_status_sig_ul_tmsi() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + + f_init(); + f_global_init(); + + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx + 1) { + /* some downlink PDU occurring on SIG-BVC with a TMSI */ + const hexstring imsi := '001010123456789'H + var OCT4 tmsi := f_gen_tmsi_for_sgsn_nri(sgsn_idx, nri_idx); + var BssgpBvci bvci := g_pcu[0].cfg.bvc[0].bvci; + var PDU_BSSGP inner := valueof(ts_BSSGP_CS_PAGING_PTMSI(bvci, imsi, oct2int(tmsi))); + f_TC_status_ul(0, sgsn_idx, inner); + } + } + + f_cleanup(); +} + + +/* STATUS in uplink direction on PTP-BVC containing a TLLI; expect routing by its NRI */ +testcase TC_status_ptp_ul_tlli() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + + f_init(); + f_global_init_ptp(); + + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx + 1) { + /* some downlink PDU occurring on PTP-BVC with a TLLI */ + var OCT4 tlli := f_gen_tlli_for_sgsn_nri(sgsn_idx, nri_idx); + var PDU_BSSGP inner := valueof(ts_BSSGP_DL_UD(tlli, '2342'O)); + + f_TC_status_ul(0, sgsn_idx, inner); + } + } + + f_cleanup(); +} + +/* STATUS in uplink direction on PTP-BVC containing a TMSI; expect routing by its NRI */ +testcase TC_status_ptp_ul_tmsi() runs on GlobalTest_CT +{ + var integer sgsn_idx, nri_idx; + + f_init(); + f_global_init_ptp(); + + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + for (nri_idx := 0; nri_idx < lengthof(mp_sgsn_nri[sgsn_idx]); nri_idx := nri_idx + 1) { + /* some downlink PDU occurring on PTP-BVC with a TMSI */ + const hexstring imsi := '001010123456789'H + var OCT4 tmsi := f_gen_tmsi_for_sgsn_nri(sgsn_idx, nri_idx); + var BssgpBvci bvci := g_pcu[0].cfg.bvc[0].bvci; + var PDU_BSSGP inner := valueof(ts_BSSGP_CS_PAGING_PTMSI(bvci, imsi, oct2int(tmsi))); + f_TC_status_ul(0, sgsn_idx, inner); + } + } + + f_cleanup(); +} + +/* STATUS in downlink direction in SIG-BVC containing a BVCI; expect routing by it */ +testcase TC_status_sig_dl_bvci() runs on GlobalTest_CT +{ + var integer sgsn_idx, pcu_idx, bvc_idx; + + f_init(); + f_global_init(); + + /* test each BVC in each PCU from each SGSN */ + for (pcu_idx := 0; pcu_idx < lengthof(g_pcu); pcu_idx := pcu_idx + 1) { + for (bvc_idx := 0; bvc_idx < lengthof(g_pcu[pcu_idx].cfg.bvc); bvc_idx := bvc_idx + 1) { + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + /* some uplink PDU occurring on SIG-BVC containing a BVCI */ + var BssgpBvci bvci := g_pcu[pcu_idx].cfg.bvc[bvc_idx].bvci; + var PDU_BSSGP inner := valueof(ts_BSSGP_LLC_DISCARDED('12345678'O, 1, bvci, 23)); + f_TC_status_dl(sgsn_idx, pcu_idx, inner); + } + } + } + + f_cleanup(); +} + +/* STATUS in downlink direction in PTP-BVC; expect routing by BVCI */ +testcase TC_status_ptp_dl_bvci() runs on GlobalTest_CT +{ + var integer sgsn_idx, pcu_idx, bvc_idx; + + f_init(); + f_global_init_ptp(); + + /* test each BVC in each PCU from each SGSN */ + for (pcu_idx := 0; pcu_idx < lengthof(g_pcu); pcu_idx := pcu_idx + 1) { + for (bvc_idx := 0; bvc_idx < lengthof(g_pcu[pcu_idx].cfg.bvc); bvc_idx := bvc_idx + 1) { + var BssgpBvci bvci := g_pcu[pcu_idx].cfg.bvc[bvc_idx].bvci; + f_global_ptp_connect_pcu_bvci(pcu_idx, bvci); + for (sgsn_idx := 0; sgsn_idx < NUM_SGSN; sgsn_idx := sgsn_idx + 1) { + f_global_ptp_connect_sgsn_bvci(sgsn_idx, bvci); + + /* some uplink PDU occurring on PTP-BVC */ + var BssgpCellId cell_id := g_pcu[pcu_idx].cfg.bvc[bvc_idx].cell_id; + var PDU_BSSGP inner := valueof(ts_BSSGP_UL_UD('12345678'O, cell_id, '4223'O)); + f_TC_status_dl(sgsn_idx, pcu_idx, inner); + } + } + } + + f_cleanup(); +} + +/* TODO: test case for DL-STATUS(SUSPEND/RESUME) containing RA-ID; expect routing by RA-ID */ +/* TODO: test case for UL-STATUS(PAGING-by-IMSI) after sending an actual PAGIN-by-IMSI in DL first */ control { execute( TC_BVC_bringup() ); + execute( TC_BVC_bringup_conflicting() ); + + execute( TC_ul_unitdata() ); + execute( TC_ul_unitdata_pool_failure() ); + execute( TC_dl_unitdata() ); + execute( TC_ra_capability() ); + execute( TC_ra_capability_upd() ); + execute( TC_radio_status() ); + execute( TC_radio_status_tmsi() ); + execute( TC_radio_status_imsi() ); + execute( TC_suspend() ); + execute( TC_resume() ); + execute( TC_trace() ); + execute( TC_llc_discarded() ); + execute( TC_overload() ); + execute( TC_bvc_block_ptp() ); + execute( TC_bvc_unblock_ptp() ); + execute( TC_bvc_reset_ptp_from_bss() ); + execute( TC_bvc_reset_sig_from_bss() ); + execute( TC_bvc_reset_ptp_from_sgsn() ); + execute( TC_bvc_reset_blocked_ptp_from_sgsn() ); + execute( TC_bvc_reset_sig_from_sgsn() ); + if (mp_enable_bss_load_sharing) { + /* don't enable this by default, as we don't yet have any automatic test setup for FR with 4 NS-VC */ + execute( TC_load_sharing_dl() ); + } + + /* PAGING-PS over PTP BVC */ + execute( TC_paging_ps_ptp_bss() ); + execute( TC_paging_ps_ptp_lac() ); + execute( TC_paging_ps_ptp_lac_unknown() ); + execute( TC_paging_ps_ptp_rac() ); + execute( TC_paging_ps_ptp_rac_unknown() ); + execute( TC_paging_ps_ptp_bvci() ); + execute( TC_paging_ps_ptp_bvci_imsi() ); + execute( TC_paging_ps_ptp_bvci_unknown() ); + execute( TC_paging_ps_reject_ptp_bvci() ); + execute( TC_paging_ps_reject_ptp_bvci_imsi() ); + execute( TC_dummy_paging_ps_ptp() ); + + /* PAGING-PS over SIG BVC */ + execute( TC_paging_ps_sig_bss() ); + execute( TC_paging_ps_sig_lac() ); + execute( TC_paging_ps_sig_lac_unknown() ); + execute( TC_paging_ps_sig_rac() ); + execute( TC_paging_ps_sig_rac_unknown() ); + execute( TC_paging_ps_sig_bvci() ); + execute( TC_paging_ps_sig_bvci_imsi() ); + execute( TC_paging_ps_sig_bvci_unknown() ); + execute( TC_paging_ps_reject_sig_bvci() ); + execute( TC_paging_ps_reject_sig_bvci_imsi() ); + execute( TC_dummy_paging_ps_sig() ); + + /* PAGING-CS over PTP BVC */ + execute( TC_paging_cs_ptp_bss() ); + execute( TC_paging_cs_ptp_lac() ); + execute( TC_paging_cs_ptp_lac_unknown() ); + execute( TC_paging_cs_ptp_rac() ); + execute( TC_paging_cs_ptp_rac_unknown() ); + execute( TC_paging_cs_ptp_bvci() ); + execute( TC_paging_cs_ptp_bvci_unknown() ); + + /* PAGING-CS over SIG BVC */ + execute( TC_paging_cs_sig_bss() ); + execute( TC_paging_cs_sig_lac() ); + execute( TC_paging_cs_sig_lac_unknown() ); + execute( TC_paging_cs_sig_rac() ); + execute( TC_paging_cs_sig_rac_unknown() ); + execute( TC_paging_cs_sig_bvci() ); + execute( TC_paging_cs_sig_bvci_unknown() ); + + /* RAN Information Management */ + execute( TC_rim_info_req() ); + execute( TC_rim_info() ); + execute( TC_rim_info_ack() ); + execute( TC_rim_info_error() ); + execute( TC_rim_info_app_error() ); + execute( TC_rim_info_pcu2pcu() ); + execute( TC_rim_from_eutran() ); + + + execute( TC_flush_ll_bvci_new() ); + + execute( TC_flush_ll_no_bvci_new() ); + + execute( TC_fc_bvc() ); + execute( TC_fc_ms() ); + execute( TC_ms_reg_enq() ); + + /* Uplink STATUS */ + execute( TC_status_sig_ul_tlli() ); + execute( TC_status_sig_ul_tmsi() ); + execute( TC_status_ptp_ul_tlli() ); + execute( TC_status_ptp_ul_tmsi() ); + + /* Downlink STATUS */ + execute( TC_status_sig_dl_bvci() ); + execute( TC_status_ptp_dl_bvci() ); } |