hnbgw: add mscpool paging tests

Change-Id: If4bbd5c970108b01e8556fa7744ff627db75fb13
diff --git a/hnbgw/HNBGW_Tests.ttcn b/hnbgw/HNBGW_Tests.ttcn
index b1f6cae..d2854d5 100644
--- a/hnbgw/HNBGW_Tests.ttcn
+++ b/hnbgw/HNBGW_Tests.ttcn
@@ -46,6 +46,7 @@
 import from RANAP_PDU_Contents all;
 import from RANAP_IEs all;
 import from RANAP_Templates all;
+import from RANAP_CodecPort all;
 
 import from RAN_Adapter all;
 import from RAN_Emulation all;
@@ -68,6 +69,8 @@
 import from L3_Templates all;
 import from L3_Common all;
 
+import from SCCPasp_Types all;
+
 const integer NUM_MSC := 4;
 const integer NUM_SGSN := 4;
 
@@ -274,7 +277,10 @@
 	boolean expect_separate_sccp_cr,
 	integer tx_sccp_cr_data_len,
 	charstring pfcp_local_addr,
-	octetstring nas_pdu optional
+	octetstring nas_pdu optional,
+	/* local and remote SCCP addresses, used in TC_mscpool_paging_* */
+	SCCP_PAR_Address sccp_addr_msc optional,
+	SCCP_PAR_Address sccp_addr_hnbgw optional
 }
 
 /* We extend:
@@ -991,7 +997,9 @@
 	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,
-	nas_pdu := omit
+	nas_pdu := omit,
+	sccp_addr_msc := omit,
+	sccp_addr_hnbgw := omit
 }
 
 /* Create an Iuh connection; send InitialUE; expect it to appear on new SCCP conenction */
@@ -2287,6 +2295,109 @@
 	f_shutdown_helper();
 }
 
+/* Make sure that whichever MSC paged a subscriber will also get the Paging Response. Page by IMSI, which would be
+ * round-robined to another MSC, to make sure the Paging->Response relation is stronger than the NRI->MSC mapping. */
+friend function f_tc_mscpool_paging_imsi(charstring id, TestHdlrParams pars) runs on ConnHdlr {
+	f_init_handler(pars);
+
+	var hexstring imsi := '001010000000123'H;
+	var RANAP_IEs.CN_DomainIndicator domain_ind;
+	if (pars.ps_domain) {
+		domain_ind := ps_domain;
+	} else {
+		domain_ind := cs_domain;
+	}
+	var template (value) RANAP_PDU paging := ts_RANAP_Paging(domain_ind, imsi_hex2oct(imsi));
+	BSSAP.send(ts_RANAP_UNITDATA_req(pars.sccp_addr_hnbgw, pars.sccp_addr_msc, paging));
+	/* TODO: Expect RUA ConnectionlessTransfer Paging (on all HNB).
+	 * We could verify the Paging sent from osmo-hnbgw to RUA with some effort,
+	 * but, this test does not care whether the Paging was forwarded to RUA or not, only that osmo-hnbgw *received*
+	 * the Paging. In the CN pool decisions, osmo-hnbgw should match up Paging Response to an earlier Paging.
+	 */
+
+	f_sleep(1.0);
+
+	/* Despite the round robin pointing at the second MSC ('roundrobin next msc 1'), the earlier Paging for the same IMSI
+	 * causes this Paging Response to go to the first MSC ('msc 0'). */
+	f_perform_compl_l3(f_gen_one_compl_l3(PAGRESP, ts_MI_IMSI_LV(imsi)));
+	f_sleep(1.0);
+}
+
+testcase TC_mscpool_paging_imsi() runs on test_CT {
+	f_init(nr_msc := 3);
+	f_sleep(1.0);
+
+	var boolean ps_domain := false;
+
+	/* Testing a Paging on the first MSC to get a Paging Response back to the first MSC. Set round robin to the
+	 * second MSC to make sure we're getting the Paging logic, not a coincidental round robin match. */
+	f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0);
+
+	f_ctrs_cn_init(ps_domain := ps_domain);
+
+	var ConnHdlr vc_conn1;
+	var template (value) TestHdlrParams pars1 := t_pars(0, ps_domain := ps_domain, cn_nr := 0);
+	pars1.sccp_addr_hnbgw := g_cn[valueof(pars1.cn_idx)].sccp_addr_peer;
+	pars1.sccp_addr_msc := g_cn[valueof(pars1.cn_idx)].sccp_addr_own;
+	vc_conn1 := f_start_handler_with_pars(refers(f_tc_mscpool_paging_imsi), pars1);
+	vc_conn1.done;
+	f_ctrs_cn_expect(0, "cnpool:subscr:paged");
+	f_shutdown_helper();
+}
+
+/* Make sure that whichever MSC paged a subscriber will also get the Paging Response.  Page by TMSI with an NRI value
+ * that matches a different MSC, to make sure the Paging->Response relation is stronger than the NRI->MSC mapping. */
+friend function f_tc_mscpool_paging_tmsi(charstring id, TestHdlrParams pars) runs on ConnHdlr {
+	f_init_handler(pars);
+
+	var hexstring imsi := '001010000000124'H;
+	var integer nri_v := 300; /* <-- second MSC's NRI */
+	var octetstring tmsi := f_gen_tmsi(suffix := 0, nri_v := nri_v);
+
+	var RANAP_IEs.CN_DomainIndicator domain_ind;
+	if (pars.ps_domain) {
+		domain_ind := ps_domain;
+	} else {
+		domain_ind := cs_domain;
+	}
+	var template (value) RANAP_PDU paging := ts_RANAP_Paging_temp_id(domain_ind, imsi_hex2oct(imsi),
+									 ts_RANAP_TemporaryUE_ID_TMSI(tmsi));
+	BSSAP.send(ts_RANAP_UNITDATA_req(pars.sccp_addr_hnbgw, pars.sccp_addr_msc, paging));
+	/* TODO: Expect RUA ConnectionlessTransfer Paging (on all HNB).
+	 * We could verify the Paging sent from osmo-hnbgw to RUA with some effort,
+	 * but, this test does not care whether the Paging was forwarded to RUA or not, only that osmo-hnbgw *received*
+	 * the Paging. In the CN pool decisions, osmo-hnbgw should match up Paging Response to an earlier Paging.
+	 */
+
+	f_sleep(1.0);
+
+	/* Despite the round robin pointing at the third MSC ('roundrobin next msc 2'), the earlier Paging for the same
+	 * TMSI causes this Paging Response to go to the first MSC ('msc 0'). */
+	f_perform_compl_l3(f_gen_one_compl_l3(PAGRESP, ts_MI_TMSI_NRI_LV(nri_v)));
+	f_sleep(1.0);
+}
+testcase TC_mscpool_paging_tmsi() runs on test_CT {
+	f_init(nr_msc := 3);
+	f_sleep(1.0);
+
+	var boolean ps_domain := false;
+
+	/* Testing a Paging on the first MSC to get a Paging Response back to the first MSC. Set round robin to the
+	 * third MSC to make sure we're getting the Paging logic, not a coincidental round robin match. */
+	f_vty_set_roundrobin_next(HNBGWVTY, ps_domain, 0);
+
+	f_ctrs_cn_init(ps_domain := ps_domain);
+
+	var ConnHdlr vc_conn1;
+	var template (value) TestHdlrParams pars1 := t_pars(0, ps_domain := ps_domain, cn_nr := 0);
+	pars1.sccp_addr_hnbgw := g_cn[valueof(pars1.cn_idx)].sccp_addr_peer;
+	pars1.sccp_addr_msc := g_cn[valueof(pars1.cn_idx)].sccp_addr_own;
+	vc_conn1 := f_start_handler_with_pars(refers(f_tc_mscpool_paging_tmsi), pars1);
+	vc_conn1.done;
+	f_ctrs_cn_expect(0, "cnpool:subscr:paged");
+	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 {
 
@@ -2450,6 +2561,8 @@
 	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_paging_imsi() );
+	execute( TC_mscpool_paging_tmsi() );
 	execute( TC_mscpool_no_allow_attach_round_robin() );
 	execute( TC_mscpool_no_allow_attach_valid_nri() );
 	execute( TC_mscpool_sccp_n_pcstate_detaches_cnlink() );
diff --git a/library/RAN_Emulation.ttcnpp b/library/RAN_Emulation.ttcnpp
index 40929ee..25727f3 100644
--- a/library/RAN_Emulation.ttcnpp
+++ b/library/RAN_Emulation.ttcnpp
@@ -168,6 +168,7 @@
 #endif
 #ifdef RAN_EMULATION_RANAP
 		RANAP_PDU,
+		RANAP_N_UNITDATA_req,
 		/* Client requests us to create SCCP Connection */
 		RANAP_Conn_Req,
 #endif
@@ -969,6 +970,7 @@
 		var RANAP_N_DISCONNECT_ind rdisc_ind;
 		var RANAP_Conn_Req creq;
 		var RANAP_PDU ranap;
+		var RANAP_N_UNITDATA_req ranap_ud;
 		var RAN_ConnHdlr vc_conn;
 		var PDU_DTAP_PS_MO ps_mo;
 		var PDU_DTAP_PS_MT ps_mt;
@@ -1034,6 +1036,11 @@
 			RANAP.send(ts_RANAP_DATA_req(conn_id, ranap));
 			}
 
+		/* e.g. for Paging from virtual MSC/SGSN to SUT osmo-hnbgw */
+		[] CLIENT.receive(RANAP_N_UNITDATA_req:?) -> value ranap_ud sender vc_conn {
+			RANAP.send(ranap_ud);
+			}
+
 		/* Disconnect request client -> SCCP */
 		[] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
 			var integer conn_id := f_conn_id_by_comp(vc_conn);
diff --git a/library/ranap/RANAP_Templates.ttcn b/library/ranap/RANAP_Templates.ttcn
index bc56bdb..051195f 100644
--- a/library/ranap/RANAP_Templates.ttcn
+++ b/library/ranap/RANAP_Templates.ttcn
@@ -720,6 +720,47 @@
 	}
 }
 
+template (value) TemporaryUE_ID ts_RANAP_TemporaryUE_ID_TMSI(octetstring tmsi) := {
+	tMSI := tmsi
+}
+
+template (value) RANAP_PDU
+ts_RANAP_Paging_temp_id(template (value) CN_DomainIndicator dom, template (value) IMSI imsi,
+			template (value) TemporaryUE_ID temp_id,
+			template (omit) Paging.protocolExtensions exts := omit) := {
+	initiatingMessage := {
+		procedureCode := id_Paging,
+		criticality := ignore,
+		value_ := {
+			paging := {
+				protocolIEs := {
+					{
+						id := id_CN_DomainIndicator,
+						criticality := ignore,
+						value_ := {
+							cN_DomainIndicator := dom
+						}
+					}, {
+						id := id_PermanentNAS_UE_ID,
+						criticality := ignore,
+						value_ := {
+							permanentNAS_UE_ID := {
+								iMSI := imsi
+							}
+						}
+					}, {
+						id := id_TemporaryUE_ID,
+						criticality := ignore,
+						value_ := {
+							temporaryUE_ID := temp_id
+						}
+					}
+				},
+				protocolExtensions := exts
+			}
+		}
+	}
+}
 
 
 /*****************************************************************************************************