diff options
author | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2023-04-20 20:43:55 +0200 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2023-08-08 04:47:06 +0200 |
commit | 7d0e680d9929d5bbbd32b07c6a9ed80f22abdddb (patch) | |
tree | 62d4b65f3c184c30c41990619b0596add95040d7 | |
parent | 3d5a0da49b0787efaae3119b183b3ce5b7a58f66 (diff) |
hnbgw: add CN pool tests
docker-playground.git needs a config file change to be committed at the
same time as this patch, see 'Related'.
Depends: osmo-ttcn3-hacks I94aa0b2adfc48b98cb4b1efe595c2432fc603d6c
Change-Id: I027a059faed3f140f8801f84338956cd004043b5
-rw-r--r-- | hnbgw/HNBGW_Tests.ttcn | 678 | ||||
-rwxr-xr-x | hnbgw/gen_links.sh | 2 | ||||
-rw-r--r-- | hnbgw/osmo-hnbgw-with-pfcp.cfg | 94 | ||||
-rw-r--r-- | hnbgw/osmo-hnbgw.cfg | 88 | ||||
-rw-r--r-- | hnbgw/osmo-stp.cfg | 41 | ||||
-rw-r--r-- | library/L3_Templates.ttcn | 51 | ||||
-rw-r--r-- | library/Osmocom_Types.ttcn | 14 |
7 files changed, 946 insertions, 22 deletions
diff --git a/hnbgw/HNBGW_Tests.ttcn b/hnbgw/HNBGW_Tests.ttcn index 92f93cf5..df6fc8e3 100644 --- a/hnbgw/HNBGW_Tests.ttcn +++ b/hnbgw/HNBGW_Tests.ttcn @@ -63,7 +63,10 @@ import from PFCP_CodecPort all; import from TCCConversion_Functions all; import from MobileL3_Types all; +import from MobileL3_CommonIE_Types all; +import from MobileL3_GMM_SM_Types all; import from L3_Templates all; +import from L3_Common all; const integer NUM_MSC := 4; const integer NUM_SGSN := 4; @@ -270,7 +273,8 @@ type record TestHdlrParams { HnbConfig hnb optional, boolean expect_separate_sccp_cr, integer tx_sccp_cr_data_len, - charstring pfcp_local_addr + charstring pfcp_local_addr, + octetstring nas_pdu optional } /* We extend: @@ -324,6 +328,11 @@ type component test_CT extends CTRL_Adapter_CT { port TELNETasp_PT HNBGWVTY; /* global test case guard timer (actual timeout value is set in f_init()) */ timer T_guard := 30.0; + + /* The cnlink type 'msc' or 'sgsn', to be used in CTRL commands to obtain counters */ + var charstring g_ctr_cn_node_name; + /* Counter state */ + var CounterNameValsList g_ctr_cn; } /* global altstep for global guard timer; */ @@ -976,7 +985,8 @@ t_pars(integer imsi_suffix, boolean ps_domain := false, integer hnb_idx := 0, hnb := omit, /* filled in later */ expect_separate_sccp_cr := expect_separate_sccp_cr, tx_sccp_cr_data_len := tx_sccp_cr_data_len, - pfcp_local_addr := mp_pfcp_ip_local + pfcp_local_addr := mp_pfcp_ip_local, + nas_pdu := omit } /* Create an Iuh connection; send InitialUE; expect it to appear on new SCCP conenction */ @@ -1761,6 +1771,648 @@ testcase TC_ps_rab_assignment_without_pfcp() runs on test_CT { f_shutdown_helper(); } +/* Default list of counters for a 'cn' entity to test the cnpool feature. */ +const CounterNameVals counternames_cnpool := { + { "cnpool:subscr:new", 0 }, + { "cnpool:subscr:known", 0 }, + { "cnpool:subscr:reattach", 0 }, + { "cnpool:subscr:attach_lost", 0 }, + { "cnpool:subscr:paged", 0 } +}; +private function f_ctrs_cn_init(boolean ps_domain, integer cn_count := 0, + CounterNameVals counternames := counternames_cnpool) runs on test_CT { + if (ps_domain) { + g_ctr_cn_node_name := "sgsn"; + if (cn_count == 0) { + cn_count := NUM_SGSN; + } + } else { + g_ctr_cn_node_name := "msc"; + if (cn_count == 0) { + cn_count := NUM_MSC; + } + } + g_ctr_cn := f_counter_name_vals_get_n(IPA_CTRL, g_ctr_cn_node_name, cn_count, counternames); + log("initial " & g_ctr_cn_node_name & " rate counters: ", g_ctr_cn); +} + +/* f_ctrs_cn_init(); + * f_do_thing(on_cn := 0); + * f_do_thing(on_cn := 0); + * f_do_other(on_cn := 1); + * f_ctrs_cn_add(0, "thing", 2); + * f_ctrs_cn_add(1, "other"); + * f_ctrs_cn_verify(); + */ +private function f_ctrs_cn_verify() runs on test_CT { + log("verifying", g_ctr_cn_node_name, " rate counters: ", g_ctr_cn); + f_counter_name_vals_expect_n(IPA_CTRL, g_ctr_cn_node_name, g_ctr_cn); +} + +/* convenience: f_ctrs_cn_add() and f_ctrs_cn_verify() in one call. + * f_ctrs_cn_init(); + * f_do_thing(on_cn := 0); + * f_do_thing(on_cn := 0); + * f_do_thing(on_cn := 0); + * f_ctrs_cn_expect(0, "thing", 3); + */ +private function f_ctrs_cn_expect(integer cn_nr, charstring countername, integer val := 1) runs on test_CT { + f_ctrs_cn_add(cn_nr, countername, val); + f_ctrs_cn_verify(); +} + +private function f_ctrs_cn_add(integer cn_nr, charstring countername, integer val := 1) runs on test_CT { + f_counter_name_vals_list_add(g_ctr_cn, cn_nr, countername, val); +} + +private function f_perform_compl_l3(octetstring nas, boolean do_clear := true, boolean expect_iu_l3 := true) +runs on ConnHdlr { + timer T := 10.0; + + /* create an expect on the Iu side for the random NAS portion */ + if (g_pars.expect_separate_sccp_cr) { + f_ran_register_sccp_cr_without_payload(); + } else { + f_ran_register_exp(nas); + } + + /* send Connect via Iuh (creating a RUA connection) */ + var RANAP_PDU tx := f_build_initial_ue_with_nas(g_pars, nas); + RUA.send(RUA_Conn_Req:{g_pars.ps_domain, tx}); + + if (expect_iu_l3) { + /* Expect same message to arrive at CN */ + f_bssap_expect(tx); + } +} + +private function f_tc_cnpool_compl_l3(charstring id, TestHdlrParams pars) runs on ConnHdlr { + f_init_handler(pars); + f_perform_compl_l3(g_pars.nas_pdu); +} + +private function f_TC_cnpool_compl_l3(boolean ps_domain, octetstring nas_pdu, integer cn_nr, + template (omit) charstring inc_countername := omit) runs on test_CT { + var ConnHdlr vc_conn; + var template (value) TestHdlrParams pars := t_pars(0, ps_domain := ps_domain, cn_nr := cn_nr); + pars.nas_pdu := nas_pdu; + log("XXX ", pars); + vc_conn := f_start_handler_with_pars(refers(f_tc_cnpool_compl_l3), pars); + vc_conn.done; + + if (not istemplatekind(inc_countername, "omit")) { + f_ctrs_cn_expect(cn_nr, valueof(inc_countername)); + } +} + +function f_TC_cnpool_compl_l3_list(boolean ps_domain, ro_octetstring compl3, Osmocom_Types.ro_integer cn_nrs, + charstring inc_countername) runs on test_CT { + var integer n := lengthof(compl3); + if (n < lengthof(cn_nrs)) { + n := lengthof(cn_nrs); + } + for (var integer i := 0; i < n; i := i + 1) { + var integer cn_nr := cn_nrs[i mod lengthof(cn_nrs)]; + f_TC_cnpool_compl_l3(ps_domain, compl3[i mod lengthof(compl3)], cn_nr, inc_countername); + } +} + +type enumerated Compl3Type { + /* CS */ + LU, + CMSERV, + PAGRESP, + IMSIDETACH, + + /* PS */ + ATTACHREQ, + RAUREQ, + DETREQ +}; + +private function f_gen_one_compl_l3(Compl3Type compl3type, template (value) MobileIdentityLV mi, + integer ps_nri := -1 + ) return octetstring +{ + /* CS */ + if (compl3type == LU) { + return enc_PDU_ML3_MS_NW(valueof(ts_LU_REQ(LU_Type_IMSI_Attach, valueof(mi), '00F110'O))); + } + if (compl3type == CMSERV) { + return enc_PDU_ML3_MS_NW(valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, valueof(mi)))); + } + if (compl3type == PAGRESP) { + return enc_PDU_ML3_MS_NW(valueof(ts_PAG_RESP(valueof(mi)))); + } + if (compl3type == IMSIDETACH) { + return enc_PDU_ML3_MS_NW(valueof(ts_ML3_MO_MM_IMSI_DET_Ind(valueof(mi)))); + } + + /* PS */ + var template (omit) NetworkResourceIdentifierContainerTLV nri := omit; + if (ps_nri >= 0) { + nri := valueof(ts_GMM_NRI(ps_nri)); + } + + if (compl3type == ATTACHREQ) { + return enc_PDU_L3_MS_SGSN(valueof(ts_GMM_ATTACH_REQ(valueof(mi), f_RAI('001'H, '01'H, '2a2a'O, '17'O), + nri := nri))); + } + if (compl3type == RAUREQ) { + return enc_PDU_L3_MS_SGSN(valueof(ts_GMM_RAU_REQ(valueof(mi), GPRS_UPD_T_PERIODIC, + f_RAI('001'H, '01'H, '2a2a'O, '17'O), + nri := nri))); + } + if (compl3type == DETREQ) { + return enc_PDU_L3_MS_SGSN(valueof(ts_GMM_DET_REQ_MO_mi(c_GMM_DTT_MO_GPRS, power_off := false, + p_tmsi := valueof(ts_MI_TLV(mi.mobileIdentityV))))); + } + + setverdict(fail, "unknown complete layer 3 type"); + mtc.stop; +} + +type record of Compl3Type ro_Compl3Type; +type record of MobileIdentityLV ro_MobileIdentityLV; +type record of octetstring ro_octetstring; + +/* Generate a list of n Complete Layer 3 NAS PDUs, + * rotating through the message kinds listed in 'types' and the mobile identities in mis. + */ +private function f_gen_compl_l3(ro_Compl3Type types, ro_MobileIdentityLV mis, integer n) return ro_octetstring +{ + var ro_octetstring res := {}; + for (var integer i := 0; i < n; i := i + 1) { + var integer ti := i mod lengthof(types); + var integer mi := i mod lengthof(mis); + res[i] := f_gen_one_compl_l3(types[ti], mis[mi]); + } + return res; +} + +private function f_gen_mi_imsi(integer n) return MobileIdentityLV +{ + return valueof(ts_MI_IMSI_LV(f_gen_imsi(n))); +} + +private function f_gen_mi_imsis(integer n) return ro_MobileIdentityLV +{ + var ro_MobileIdentityLV mis := {}; + for (var integer i := 0; i < n; i := i + 1) { + mis[i] := f_gen_mi_imsi(n); + } + return mis; +} + +function f_vty_set_roundrobin_next(TELNETasp_PT VTY, boolean ps_domain, integer cn_nr) +{ + var charstring msc_sgsn; + if (ps_domain) { + msc_sgsn := "sgsn"; + } else { + msc_sgsn := "msc"; + } + f_vty_transceive(VTY, "cnpool roundrobin next " & msc_sgsn & " " & int2str(cn_nr)); +} + +private function f_gen_compl3_by_domain(boolean ps_domain, integer n, template (omit) ro_MobileIdentityLV mis := omit) return ro_octetstring{ + var ro_Compl3Type types; + if (ps_domain) { + types := { ATTACHREQ, RAUREQ, DETREQ }; + } else { + types := { LU, CMSERV, PAGRESP, IMSIDETACH }; + } + if (istemplatekind(mis, "omit")) { + mis := f_gen_mi_imsis(n); + } + return f_gen_compl_l3(types, valueof(mis), n); +} + +/* Various Complete Layer 3 by IMSI all end up with the first MSC, because the other MSCs are not connected. */ +testcase TC_mscpool_L3Compl_on_1_cnlink() runs on test_CT { + f_TC_cnpool_L3Compl_on_1_cnlink(ps_domain := false); +} +testcase TC_sgsnpool_L3Compl_on_1_cnlink() runs on test_CT { + f_TC_cnpool_L3Compl_on_1_cnlink(ps_domain := true); +} +function f_TC_cnpool_L3Compl_on_1_cnlink(boolean ps_domain) runs on test_CT { + + f_init(); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 4); + f_TC_cnpool_compl_l3_list(ps_domain, compl3, {0, 0, 0, 0}, "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* Three Layer 3 Complete by IMSI are round-robin'ed across two connected MSCs */ +testcase TC_mscpool_L3Complete_by_imsi_round_robin() runs on test_CT { + f_TC_cnpool_L3Complete_by_imsi_round_robin(ps_domain := false); +} +testcase TC_sgsnpool_L3Complete_no_nri_round_robin() runs on test_CT { + f_TC_cnpool_L3Complete_by_imsi_round_robin(ps_domain := true); +} +function f_TC_cnpool_L3Complete_by_imsi_round_robin(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3); + + f_TC_cnpool_compl_l3_list(ps_domain, compl3, + /* Third Complete Layer 3 wraps back to msc 0 */ + cn_nrs := {0, 1, 0}, + inc_countername := "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* Three LU by TMSI are round-robin'ed across two connected MSCs, because they contain a NULL-NRI (0, 1) + * (configured in osmo-hnbgw.cfg). */ +testcase TC_mscpool_LU_by_tmsi_null_nri_0_round_robin() runs on test_CT { + f_TC_cnpool_LU_by_tmsi_null_nri_N_round_robin(ps_domain := false, nri_val := 0); +} +/* For NRI == 1, one of the MSC also has the NULL-NRI as part of its owned NRIs, but the NULL-NRI setting is stronger + * than that. */ +testcase TC_mscpool_LU_by_tmsi_null_nri_1_round_robin() runs on test_CT { + f_TC_cnpool_LU_by_tmsi_null_nri_N_round_robin(ps_domain := false, nri_val := 1); +} +function f_TC_cnpool_LU_by_tmsi_null_nri_N_round_robin(boolean ps_domain, integer nri_val) runs on test_CT { + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_MobileIdentityLV mis := { valueof(ts_MI_TMSI_NRI_LV(nri_val)) }; + var ro_octetstring compl3; + if (ps_domain) { + compl3 := { + f_gen_one_compl_l3(ATTACHREQ, mis[0], nri_val) + }; + } else { + compl3 := f_gen_compl_l3({LU}, mis, 1); + } + + f_TC_cnpool_compl_l3_list(ps_domain, compl3, + /* The third Complete Layer 3 wraps back to msc 0 */ + {0, 1, 0}, + "cnpool:subscr:reattach"); + f_shutdown_helper(); +} + +/* Three Layer 3 Complete by TMSI are round-robin'ed across two connected MSCs, because they contain an NRI not + * assigned to any MSC (configured in osmo-hnbgw.cfg). */ +testcase TC_mscpool_L3Complete_by_tmsi_unassigned_nri_round_robin() runs on test_CT { + f_TC_cnpool_L3Complete_by_tmsi_unassigned_nri_round_robin(ps_domain := false); +} +function f_TC_cnpool_L3Complete_by_tmsi_unassigned_nri_round_robin(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + /* 3 NRIs that are not assigned to any MSC */ + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(1000)), + valueof(ts_MI_TMSI_NRI_LV(768)), + valueof(ts_MI_TMSI_NRI_LV(819)) + }; + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3, mis); + f_TC_cnpool_compl_l3_list(ps_domain, compl3, { 0, 1, 0 }, "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* Three Layer 3 Complete by TMSI are round-robin'ed across two connected MSCs, because they contain an NRI + * assigned to a CN link that is currently not connected (configured in osmo-hnbgw.cfg). */ +testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin() runs on test_CT { + f_TC_cnpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin(ps_domain := false); +} +function f_TC_cnpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + /* 3 NRIs that are assigned to an unconnected MSC */ + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(512)), + valueof(ts_MI_TMSI_NRI_LV(767)), + valueof(ts_MI_TMSI_NRI_LV(750)) + }; + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3, mis); + + f_TC_cnpool_compl_l3(ps_domain, compl3[0], cn_nr := 0); + f_ctrs_cn_add(2, "cnpool:subscr:attach_lost"); + f_ctrs_cn_add(0, "cnpool:subscr:new"); + f_ctrs_cn_verify(); + + f_TC_cnpool_compl_l3(ps_domain, compl3[1], cn_nr := 1); + f_ctrs_cn_add(2, "cnpool:subscr:attach_lost"); + f_ctrs_cn_add(1, "cnpool:subscr:new"); + f_ctrs_cn_verify(); + + f_TC_cnpool_compl_l3(ps_domain, compl3[2], cn_nr := 0); + f_ctrs_cn_add(2, "cnpool:subscr:attach_lost"); + f_ctrs_cn_add(0, "cnpool:subscr:new"); + f_ctrs_cn_verify(); + + f_shutdown_helper(); +} + +/* Three Layer 3 Complete by TMSI with valid NRI for the second MSC are all directed to the second MSC (configured in + * osmo-hnbgw.cfg). */ +testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_1() runs on test_CT { + f_TC_cnpool_L3Complete_valid_nri_1(ps_domain := false); +} +testcase TC_sgsnpool_L3Complete_valid_nri_1() runs on test_CT { + f_TC_cnpool_L3Complete_valid_nri_1(ps_domain := true); +} +function f_TC_cnpool_L3Complete_valid_nri_1(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* All TMSIs in this test point at the second MSC, set the round robin to point at the first MSC to make sure + * this is not using round-robin. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3; + + /* 3 NRIs of the second MSC's range (256-511) */ + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(256)), + valueof(ts_MI_TMSI_NRI_LV(260)), + valueof(ts_MI_TMSI_NRI_LV(511)) + }; + if (ps_domain) { + compl3 := { + f_gen_one_compl_l3(RAUREQ, mis[0], 256), + f_gen_one_compl_l3(RAUREQ, mis[1], 260), + f_gen_one_compl_l3(RAUREQ, mis[2], 511) + }; + } else { + compl3 := f_gen_compl3_by_domain(ps_domain, 3, mis); + } + + f_TC_cnpool_compl_l3_list(ps_domain, compl3, {1, 1, 1}, "cnpool:subscr:known"); + + f_shutdown_helper(); +} + +/* Layer 3 Complete by TMSI with valid NRI for the third MSC are directed to the third MSC (configured in osmo-hnbgw.cfg), + * while a round-robin remains unaffected by that. */ +testcase TC_mscpool_L3Complete_by_tmsi_valid_nri_2() runs on test_CT { + f_TC_cnpool_L3Complete_valid_nri_2(ps_domain := false); +} +testcase TC_sgsnpool_L3Complete_valid_nri_2() runs on test_CT { + f_TC_cnpool_L3Complete_valid_nri_2(ps_domain := true); +} +function f_TC_cnpool_L3Complete_valid_nri_2(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 3, nr_sgsn := 3); + f_sleep(1.0); + + /* All TMSIs in this test point at the third MSC, set the round robin to point at the second MSC to make sure + * this is not using round-robin. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 1); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3; + + /* 2 NRIs of the third MSC's range (512-767) */ + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(512)), + valueof(ts_MI_TMSI_NRI_LV(678)) + }; + if (ps_domain) { + compl3 := { + f_gen_one_compl_l3(ATTACHREQ, mis[0], 512), + f_gen_one_compl_l3(ATTACHREQ, mis[1], 678) + }; + } else { + compl3 := f_gen_compl3_by_domain(ps_domain, 2, mis); + } + + f_TC_cnpool_compl_l3_list(ps_domain, compl3, {2, 2}, "cnpool:subscr:known"); + + /* The above forwardings to third MSC have not affected the round robin, which still points at the second MSC */ + f_TC_cnpool_compl_l3_list(ps_domain, f_gen_compl3_by_domain(ps_domain, 1), {1}, "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* LU with a TMSI but indicating a different PLMN in its previous LAI: ignore the NRI. */ +testcase TC_mscpool_LU_by_tmsi_from_other_PLMN() runs on test_CT { + f_TC_cnpool_nri_from_other_PLMN(ps_domain := false); +} +testcase TC_sgsnpool_nri_from_other_PLMN() runs on test_CT { + f_TC_cnpool_nri_from_other_PLMN(ps_domain := true); +} +function f_TC_cnpool_nri_from_other_PLMN(boolean ps_domain) runs on test_CT { + + f_init(nr_msc := 3, nr_sgsn := 3); + f_sleep(1.0); + + /* The TMSIs in this test points at the second MSC, but since it is from a different PLMN, round-robin is used + * instead, and hits msc 0. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3; + + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(260)), + valueof(ts_MI_TMSI_NRI_LV(555)) + }; + if (ps_domain) { + compl3 := { + /* An NRI of the second MSC's range (256-511), but a PLMN that doesn't match with osmo-hnbgw.cfg */ + enc_PDU_L3_MS_SGSN(valueof(ts_GMM_ATTACH_REQ(mis[0], f_RAI('999'H, '99'H, '2a2a'O, + '17'O), + nri := ts_GMM_NRI(260) + ))), + /* An NRI of the third MSC's range (512-767) and a matching PLMN gets directed by NRI. */ + f_gen_one_compl_l3(ATTACHREQ, mis[1], 555) + } + } else { + compl3 := { + /* An NRI of the second MSC's range (256-511), but a PLMN that doesn't match with osmo-hnbgw.cfg */ + enc_PDU_ML3_MS_NW(valueof(ts_LU_REQ(LU_Type_IMSI_Attach, mis[0], '99F999'O))), + /* An NRI of the third MSC's range (512-767) and a matching PLMN gets directed by NRI. */ + enc_PDU_ML3_MS_NW(valueof(ts_LU_REQ(LU_Type_IMSI_Attach, mis[1], '00F110'O))) + }; + } + + /* Foreign NRI: roundrobin */ + f_TC_cnpool_compl_l3(ps_domain, compl3[0], cn_nr := 0, inc_countername := "cnpool:subscr:new"); + + /* Local NRI: matching msc */ + f_TC_cnpool_compl_l3(ps_domain, compl3[1], cn_nr := 2, inc_countername := "cnpool:subscr:known"); + + f_shutdown_helper(); +} + +/* For round-robin, skip a CN link that has 'no allow-attach' set. */ +testcase TC_mscpool_no_allow_attach_round_robin() runs on test_CT { + + f_init(nr_msc := 3); + f_sleep(1.0); + + var boolean ps_domain := false; + + /* Mark the second MSC as offloading, round-robin should skip this MSC now. */ + f_vty_cnlink_allow_attach(HNBGWVTY, ps_domain, {true, false, true}); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + f_TC_cnpool_compl_l3_list(ps_domain, f_gen_compl3_by_domain(ps_domain, 3), + /* msc 1 is skipped */ + {0, 2, 0}, + "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* An MSC that has 'no allow-attach' set should still serve subscribers that are already attached according to their + * TMSI NRI. */ +testcase TC_mscpool_no_allow_attach_valid_nri() runs on test_CT { + + f_init(nr_msc := 3); + f_sleep(1.0); + + var boolean ps_domain := false; + + /* Mark the second MSC as offloading, round-robin should skip this MSC now. */ + f_vty_cnlink_allow_attach(HNBGWVTY, ps_domain, {true, false, true}); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_MobileIdentityLV mis := { + valueof(ts_MI_TMSI_NRI_LV(260)), + valueof(ts_MI_IMSI_LV('001010000000002'H)), + valueof(ts_MI_IMSI_LV('001010000000003'H)) + }; + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3, mis); + + /* Round robin points at msc 0, but the valid NRI directs to msc 1, even though msc 1 has 'no allow-attach'. */ + f_TC_cnpool_compl_l3(ps_domain, compl3[0], cn_nr := 1, inc_countername := "cnpool:subscr:known"); + + /* Normal round robin skips msc 1, because it has 'no allow-attach' */ + f_TC_cnpool_compl_l3(ps_domain, compl3[1], cn_nr := 0, inc_countername := "cnpool:subscr:new"); + f_TC_cnpool_compl_l3(ps_domain, compl3[2], cn_nr := 2, inc_countername := "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* When a peer point-code gets an SCCP N-PCSTATE saying it is unreachable, immediately mark the CN link as unusable. */ +testcase TC_mscpool_sccp_n_pcstate_detaches_cnlink() runs on test_CT { + f_TC_cnpool_sccp_n_pcstate_detaches_cnlink(ps_domain := false); +} +testcase TC_sgsnpool_sccp_n_pcstate_detaches_cnlink() runs on test_CT { + f_TC_cnpool_sccp_n_pcstate_detaches_cnlink(ps_domain := true); +} +function f_TC_cnpool_sccp_n_pcstate_detaches_cnlink(boolean ps_domain) runs on test_CT +{ + + f_init(nr_msc := 2, nr_sgsn := 2); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + f_ctrs_cn_init(ps_domain := ps_domain); + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3); + + f_TC_cnpool_compl_l3(ps_domain, compl3[0], cn_nr := 0, inc_countername := "cnpool:subscr:new"); + f_TC_cnpool_compl_l3(ps_domain, compl3[1], cn_nr := 1, inc_countername := "cnpool:subscr:new"); + + f_logp(HNBGWVTY, "disconnecting msc0"); + f_cn_idx_disconnect(f_cn_idx(ps_domain, 0)); + + /* Now round-robin would wrap to the first MSC, but since the first MSC is disconnected, it wraps around to the + * second. */ + f_TC_cnpool_compl_l3(ps_domain, compl3[2], cn_nr := 1, inc_countername := "cnpool:subscr:new"); + + f_shutdown_helper(); +} + +/* When a CN link point-code gets an SCCP N-PCSTATE saying it is now reachable, immediately trigger RESET and bring up the + * MSC. */ +testcase TC_mscpool_sccp_n_pcstate_attaches_cnlink() runs on test_CT { + f_TC_cnpool_sccp_n_pcstate_attaches_cnlink(ps_domain := false); +} +testcase TC_sgsnpool_sccp_n_pcstate_attaches_cnlink() runs on test_CT { + f_TC_cnpool_sccp_n_pcstate_attaches_cnlink(ps_domain := true); +} +function f_TC_cnpool_sccp_n_pcstate_attaches_cnlink(boolean ps_domain) runs on test_CT +{ + f_init(nr_msc := 1, nr_sgsn := 1); + f_sleep(1.0); + + /* Control which MSC gets chosen next by the round-robin, otherwise + * would be randomly affected by which other tests ran before this. */ + f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0); + + var ro_octetstring compl3 := f_gen_compl3_by_domain(ps_domain, 3); + + f_ctrs_cn_init(ps_domain := ps_domain); + + /* There is only one MSC, round robin stays on msc0 */ + f_TC_cnpool_compl_l3(ps_domain := ps_domain, nas_pdu := compl3[0], cn_nr := 0, inc_countername := "cnpool:subscr:new"); + f_TC_cnpool_compl_l3(ps_domain := ps_domain, nas_pdu := compl3[1], cn_nr := 0, inc_countername := "cnpool:subscr:new"); + + f_logp(HNBGWVTY, "connecting cnlink 1"); + f_cn_nr_init(ps_domain, 1); + f_vty_cnlink_allow_attach(HNBGWVTY, ps_domain, { true, true }); + f_sleep(1.0); + + /* This time round-robin wraps to the second MSC, because it is now online. */ + f_TC_cnpool_compl_l3(ps_domain := ps_domain, nas_pdu := compl3[2], cn_nr := 1, inc_countername := "cnpool:subscr:new"); + + f_shutdown_helper(); +} + control { execute(TC_hnb_register()); execute(TC_hnb_register_duplicate()); @@ -1784,6 +2436,28 @@ control { execute(TC_ps_rab_assignment_without_pfcp()); } + execute( TC_mscpool_L3Compl_on_1_cnlink() ); + execute( TC_mscpool_L3Complete_by_imsi_round_robin() ); + execute( TC_mscpool_LU_by_tmsi_null_nri_0_round_robin() ); + execute( TC_mscpool_LU_by_tmsi_null_nri_1_round_robin() ); + execute( TC_mscpool_L3Complete_by_tmsi_unassigned_nri_round_robin() ); + execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_msc_not_connected_round_robin() ); + execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_1() ); + execute( TC_mscpool_L3Complete_by_tmsi_valid_nri_2() ); + execute( TC_mscpool_LU_by_tmsi_from_other_PLMN() ); + execute( TC_mscpool_no_allow_attach_round_robin() ); + execute( TC_mscpool_no_allow_attach_valid_nri() ); + execute( TC_mscpool_sccp_n_pcstate_detaches_cnlink() ); + execute( TC_mscpool_sccp_n_pcstate_attaches_cnlink() ); + + execute( TC_sgsnpool_L3Compl_on_1_cnlink() ); + execute( TC_sgsnpool_L3Complete_no_nri_round_robin() ); + execute( TC_sgsnpool_L3Complete_valid_nri_1() ); + execute( TC_sgsnpool_L3Complete_valid_nri_2() ); + execute( TC_sgsnpool_nri_from_other_PLMN() ); + execute( TC_sgsnpool_sccp_n_pcstate_detaches_cnlink() ); + execute( TC_sgsnpool_sccp_n_pcstate_attaches_cnlink() ); + /* Run at the end since it makes osmo-hnbgw <= 1.3.0 crash: OS#5676 */ execute(TC_hnb_reregister_reuse_sctp_assoc()); } diff --git a/hnbgw/gen_links.sh b/hnbgw/gen_links.sh index 17addbd4..b6511133 100755 --- a/hnbgw/gen_links.sh +++ b/hnbgw/gen_links.sh @@ -103,7 +103,7 @@ FILES+="RAN_Adapter.ttcnpp RAN_Emulation.ttcnpp BSSAP_CodecPort.ttcn SCCP_Templa FILES+="PFCP_CodecPort.ttcn PFCP_CodecPort_CtrlFunct.ttcn PFCP_CodecPort_CtrlFunctDef.cc PFCP_Emulation.ttcn PFCP_Templates.ttcn " FILES+="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc RTP_Emulation.ttcn IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn " FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc StatsD_Checker.ttcn " -FILES+="L3_Templates.ttcn " +FILES+="L3_Templates.ttcn L3_Common.ttcn " gen_links $DIR $FILES diff --git a/hnbgw/osmo-hnbgw-with-pfcp.cfg b/hnbgw/osmo-hnbgw-with-pfcp.cfg index 178369ae..550657b4 100644 --- a/hnbgw/osmo-hnbgw-with-pfcp.cfg +++ b/hnbgw/osmo-hnbgw-with-pfcp.cfg @@ -20,30 +20,112 @@ log stderr logging level lpfcp info logging level lsua notice logging level lsccp notice + logging print timestamp date ! line vty no login ! cs7 instance 0 point-code 0.23.5 - sccp-address msc + + sccp-address msc-naught routing-indicator PC point-code 0.23.4 - sccp-address sgsn + + sccp-address msc-one + routing-indicator PC + point-code 0.0.2 + + sccp-address msc-two + routing-indicator PC + point-code 0.0.3 + + sccp-address msc-three + routing-indicator PC + point-code 0.0.4 + + sccp-address sgsn-naught routing-indicator PC point-code 0.23.1 + + sccp-address sgsn-one + routing-indicator PC + point-code 0.1.2 + + sccp-address sgsn-two + routing-indicator PC + point-code 0.1.3 + + sccp-address sgsn-three + routing-indicator PC + point-code 0.1.4 + hnbgw log-prefix hnb-id iuh local-ip 127.0.0.1 local-port 29169 hnbap-allow-tmsi 1 - iucs - remote-addr msc - iups - remote-addr sgsn pfcp remote-addr 127.0.0.1 local-addr 127.0.0.2 local-port 8805 timer pfcp x26 5 + +msc 0 + remote-addr msc-naught + +msc 1 + remote-addr msc-one + +msc 2 + remote-addr msc-two + +msc 3 + remote-addr msc-three + +sgsn 0 + remote-addr sgsn-naught + +sgsn 1 + remote-addr sgsn-one + +sgsn 2 + remote-addr sgsn-two + +sgsn 3 + remote-addr sgsn-three + +hnbgw + iucs + nri bitlen 10 + # a NULL NRI that is outside the NRI ranges used by the MSCs: + nri null add 0 + # a NULL NRI that is also used by an MSC: + nri null add 1 +msc 0 + nri add 1 255 +msc 1 + nri add 256 511 +msc 2 + nri add 512 767 + # range 768-1000 is not assigned to any MSC on purpose +msc 3 + nri add 1001 1023 + +hnbgw + iups + nri bitlen 10 + # a NULL NRI that is outside the NRI ranges used by the SGSNs: + nri null add 0 + # a NULL NRI that is also used by an SGSN: + nri null add 1 +sgsn 0 + nri add 1 255 +sgsn 1 + nri add 256 511 +sgsn 2 + nri add 512 767 + # range 768-1000 is not assigned to any SGSN on purpose +sgsn 3 + nri add 1001 1023 diff --git a/hnbgw/osmo-hnbgw.cfg b/hnbgw/osmo-hnbgw.cfg index f11f6136..3ccc28d9 100644 --- a/hnbgw/osmo-hnbgw.cfg +++ b/hnbgw/osmo-hnbgw.cfg @@ -15,21 +15,97 @@ log stderr line vty no login ! + cs7 instance 0 point-code 0.23.5 - sccp-address msc - routing-indicator PC + + sccp-address msc-naught point-code 0.23.4 - sccp-address sgsn - routing-indicator PC + + sccp-address msc-one + point-code 0.0.2 + + sccp-address msc-two + point-code 0.0.3 + + sccp-address msc-three + point-code 0.0.4 + + sccp-address sgsn-naught point-code 0.23.1 + + sccp-address sgsn-one + point-code 0.1.2 + + sccp-address sgsn-two + point-code 0.1.3 + + sccp-address sgsn-three + point-code 0.1.4 + hnbgw log-prefix hnb-id iuh local-ip 127.0.0.1 local-port 29169 hnbap-allow-tmsi 1 + # don't spam RANAP RESET messages for up to eight CN links across all tests + timer hnbgw T4 60 + +msc 0 + remote-addr msc-naught + +msc 1 + remote-addr msc-one + +msc 2 + remote-addr msc-two + +msc 3 + remote-addr msc-three + +sgsn 0 + remote-addr sgsn-naught + +sgsn 1 + remote-addr sgsn-one + +sgsn 2 + remote-addr sgsn-two + +sgsn 3 + remote-addr sgsn-three + +hnbgw iucs - remote-addr msc + nri bitlen 10 + # a NULL NRI that is outside the NRI ranges used by the MSCs: + nri null add 0 + # a NULL NRI that is also used by an MSC: + nri null add 1 +msc 0 + nri add 1 255 +msc 1 + nri add 256 511 +msc 2 + nri add 512 767 + # range 768-1000 is not assigned to any MSC on purpose +msc 3 + nri add 1001 1023 + +hnbgw iups - remote-addr sgsn + nri bitlen 10 + # a NULL NRI that is outside the NRI ranges used by the SGSNs: + nri null add 0 + # a NULL NRI that is also used by an SGSN: + nri null add 1 +sgsn 0 + nri add 1 255 +sgsn 1 + nri add 256 511 +sgsn 2 + nri add 512 767 + # range 768-1000 is not assigned to any SGSN on purpose +sgsn 3 + nri add 1001 1023 diff --git a/hnbgw/osmo-stp.cfg b/hnbgw/osmo-stp.cfg index 651975b5..e4b80d43 100644 --- a/hnbgw/osmo-stp.cfg +++ b/hnbgw/osmo-stp.cfg @@ -39,6 +39,25 @@ cs7 instance 0 as virt-msc0 m3ua asp virt-msc0-0 routing-key 1 0.23.4 + + asp virt-msc1-0 23907 2905 m3ua + local-ip 127.0.0.1 + remote-ip 127.0.0.1 + role sg + sctp-role server + as virt-msc1 m3ua + asp virt-msc1-0 + routing-key 3 0.0.2 + + asp virt-msc2-0 23909 2905 m3ua + local-ip 127.0.0.1 + remote-ip 127.0.0.1 + role sg + sctp-role server + as virt-msc2 m3ua + asp virt-msc2-0 + routing-key 5 0.0.3 + asp virt-sgsn0-0 23906 2905 m3ua local-ip 127.0.0.1 remote-ip 127.0.0.1 @@ -48,8 +67,30 @@ cs7 instance 0 asp virt-sgsn0-0 routing-key 2 0.23.1 + asp virt-sgsn1-0 23908 2905 m3ua + local-ip 127.0.0.1 + remote-ip 127.0.0.1 + role sg + sctp-role server + as virt-sgsn1 m3ua + asp virt-sgsn1-0 + routing-key 4 0.1.2 + + asp virt-sgsn2-0 23910 2905 m3ua + local-ip 127.0.0.1 + remote-ip 127.0.0.1 + role sg + sctp-role server + as virt-sgsn2 m3ua + asp virt-sgsn2-0 + routing-key 6 0.1.3 + route-table system update route 0.23.4 7.255.7 linkset virt-msc0 + update route 0.0.2 7.255.7 linkset virt-msc1 + update route 0.0.3 7.255.7 linkset virt-msc2 update route 0.23.1 7.255.7 linkset virt-sgsn0 + update route 0.1.2 7.255.7 linkset virt-sgsn1 + update route 0.1.3 7.255.7 linkset virt-sgsn2 listen m3ua 2905 accept-asp-connections dynamic-permitted diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn index c31ce1f2..710b0eab 100644 --- a/library/L3_Templates.ttcn +++ b/library/L3_Templates.ttcn @@ -177,6 +177,9 @@ function ts_MI_TMSI_TLV(template (omit) OCT4 tmsi) return template (omit) Mobile } } +template MobileIdentityLV ts_MI_TMSI_NRI_LV(integer nri_v, integer nri_bitlen := 10) := + ts_MI_TMSI_LV(tmsi := f_gen_tmsi(suffix := 0, nri_v := nri_v, nri_bitlen := nri_bitlen)); + template MobileIdentityTLV ts_MI_IMEISV_TLV(hexstring imeisv) := { elementIdentifier := '0100011'B, spare1 := '0'B, @@ -2425,11 +2428,23 @@ template (value) MSRadioAccessCapabilityLV ts_MS_RaCapa := { } } +template (value) NetworkResourceIdentifierContainerTLV ts_GMM_NRI(integer nri) := { + elementIdentifier := '10'O, + networkResourceIdentifierContainerLV := { + lengthIndicator := 2, + networkResourceIdentifierContainerV := { + nRIContainerValue := f_bits_reversed(int2bit(nri, 10)), + spare := '000000'B + } + } +} + template (value) PDU_L3_MS_SGSN ts_GMM_ATTACH_REQ(MobileIdentityLV mi_lv, RoutingAreaIdentificationV old_ra, boolean combined := false, boolean follow_on_pending := false, - template (omit) MobileStationClassmark2_TLV cm2_tlv, - template (omit) MobileStationClassmark3_TLV cm3_tlv + template (omit) MobileStationClassmark2_TLV cm2_tlv := omit, + template (omit) MobileStationClassmark3_TLV cm3_tlv := omit, + template (omit) NetworkResourceIdentifierContainerTLV nri := omit ) := { discriminator := '0000'B, /* overwritten */ tiOrSkip := { @@ -2462,7 +2477,7 @@ template (value) PDU_L3_MS_SGSN mS_NetworkFeatureSupport := omit, oldLocationAreaIdentification := omit, additionalUpdateType := omit, - tMSIBasedNRIcontainer := omit, + tMSIBasedNRIcontainer := nri, t3324 := omit, t3312_ExtendedValue := omit, extendedDRXParameters := omit @@ -2577,9 +2592,10 @@ template (value) PDU_L3_MS_SGSN ts_GMM_RAU_REQ(MobileIdentityLV mi_lv, GprsUpdateType upd_type, RoutingAreaIdentificationV old_ra, boolean follow_on_pending := false, - template (omit) MobileStationClassmark2_TLV cm2_tlv, - template (omit) MobileStationClassmark3_TLV cm3_tlv, - template (omit) OCT4 p_tmsi := omit + template (omit) MobileStationClassmark2_TLV cm2_tlv := omit, + template (omit) MobileStationClassmark3_TLV cm3_tlv := omit, + template (omit) OCT4 p_tmsi := omit, + template (omit) NetworkResourceIdentifierContainerTLV nri := omit ) := { discriminator := '0000'B, /* overwritten */ tiOrSkip := { @@ -2614,7 +2630,7 @@ template (value) PDU_L3_MS_SGSN mS_NetworkFeatureSupport := omit, oldLocationAreaIdentification := omit, additionalUpdateType := omit, - tMSIBasedNRIcontainer := omit, + tMSIBasedNRIcontainer := nri, t3324 := omit, t3312_ExtendedValue := omit, extendedDRXParameters := omit @@ -2898,6 +2914,27 @@ template (value) PDU_L3_MS_SGSN ts_GMM_DET_REQ_MO(BIT3 dtt := c_GMM_DTT_MO_GPRS, } } +template (value) PDU_L3_MS_SGSN ts_GMM_DET_REQ_MO_mi(BIT3 dtt := c_GMM_DTT_MO_GPRS, + boolean power_off := false, + template (value) MobileIdentityTLV p_tmsi, + template (omit) OCT3 p_tmsi_sig := omit) := { + discriminator := '0000'B, /* overwritten */ + tiOrSkip := { + skipIndicator := '0000'B + }, + msgs := { + gprs_mm := { + detachRequest_MS_SGSN := { + messageType := '00000000'B, /* overwritten */ + detachType := valueof(ts_GMM_DetType(dtt, power_off)), + spare := '0000'B, + ptmsi := p_tmsi, + ptmsiSignature := ts_PtmsiSigTLV(p_tmsi_sig) + } + } + } +} + template PDU_L3_SGSN_MS tr_GMM_DET_ACCEPT_MT := { discriminator := '1000'B, tiOrSkip := { diff --git a/library/Osmocom_Types.ttcn b/library/Osmocom_Types.ttcn index 52490cab..88926319 100644 --- a/library/Osmocom_Types.ttcn +++ b/library/Osmocom_Types.ttcn @@ -344,5 +344,19 @@ function f_bool2str(boolean val) return charstring { } } +/* Return a reversed bitstring */ +function f_bits_reversed(in bitstring bits) return bitstring { + for (var integer i := 0; i < lengthof(bits) / 2; i := i + 1) { + var integer reverse_i := lengthof(bits) - 1 - i; + if (i >= reverse_i) { + break; + } + var bitstring tmp; + tmp[0] := bits[i]; + bits[i] := bits[reverse_i]; + bits[reverse_i] := tmp[0]; + } + return bits; +} } with { encode "RAW"; variant "FIELDORDER(msb)" } |