cbc: Support mme/bsc sctp/tcp server mode

Related: OS#4945
Change-Id: I9fa4ddfa18ac85644f219874e6b2166e1795e3a9
diff --git a/cbc/BSC_ConnectionHandler.ttcn b/cbc/BSC_ConnectionHandler.ttcn
index ec86f01..a8601a6 100644
--- a/cbc/BSC_ConnectionHandler.ttcn
+++ b/cbc/BSC_ConnectionHandler.ttcn
@@ -25,15 +25,23 @@
 
 type function void_fn() runs on BSC_ConnHdlr;
 
+/* Coordinate with test_CT: */
+type port BSC_ConnHdlr_Coord_PT message {
+	inout charstring;
+} with { extension "internal" };
+
 /* this component represents a single subscriber connection */
 type component BSC_ConnHdlr extends CBSP_Adapter_CT {
 	var BSC_ConnHdlrPars g_pars;
+	port BSC_ConnHdlr_Coord_PT COORD;
 }
 
 type record BSC_ConnHdlrPars {
+	charstring bsc_host,
 	integer bsc_cbsp_port,
 	charstring cbc_host,
 	integer cbc_cbsp_port,
+	boolean tcp_is_client,
 	void_fn start_fn,
 	CBS_Message exp_cbs_msg optional,
 	BSSMAP_FIELD_CellIdentificationList cell_list_success optional
@@ -41,7 +49,13 @@
 
 function f_BSC_ConnHdlr_main(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
 	g_pars := pars;
-	CBSP_Adapter.f_connect(g_pars.cbc_host, g_pars.cbc_cbsp_port, "", g_pars.bsc_cbsp_port);
+	if (g_pars.tcp_is_client) {
+		CBSP_Adapter.f_connect(g_pars.cbc_host, g_pars.cbc_cbsp_port,
+					g_pars.bsc_host, g_pars.bsc_cbsp_port);
+	} else {
+		CBSP_Adapter.f_bind(g_pars.bsc_host, g_pars.bsc_cbsp_port);
+		CBSP_Adapter.f_wait_client_connect();
+	}
 
 	var BSSMAP_FIELD_CellIdentificationList cell_list := {
 		cIl_allInBSS := ''O
@@ -51,6 +65,7 @@
 	f_cbsp_send(ts_CBSP_RESTART(cell_list, CBSP_BC_MSGT_CBS, CBSP_RI_DATA_LOST));
 	f_cbsp_send(ts_CBSP_RESTART(cell_list, CBSP_BC_MSGT_EMERG, CBSP_RI_DATA_LOST));
 	as_cbsp_reset(0);
+	COORD.send(COORD_MSG_CONNECTED);
 
 	g_pars.start_fn.apply();
 }
diff --git a/cbc/CBC_Tests.ttcn b/cbc/CBC_Tests.ttcn
index 64cf5e2..406baed 100644
--- a/cbc/CBC_Tests.ttcn
+++ b/cbc/CBC_Tests.ttcn
@@ -33,18 +33,38 @@
 import from BSC_ConnectionHandler all;
 import from MME_ConnectionHandler all;
 
+const integer MAX_BSC := 2;
+const integer MAX_MME := 2;
+
+type record BSC_modulepar_cfg {
+	boolean tcp_is_client
+};
+type record of BSC_modulepar_cfg BSC_modulepar_cfgs;
+
+type record MME_modulepar_cfg {
+	boolean sctp_is_client
+};
+type record of MME_modulepar_cfg MME_modulepar_cfgs;
+
 modulepar {
+	charstring mp_local_host := "127.0.0.2";
 	charstring mp_cbc_host := "127.0.0.1";
 	integer mp_cbc_cbsp_port := 48049;
 	integer mp_cbc_sbcap_port := c_SBC_AP_PORT;
 	integer mp_cbc_ecbe_port := 12345;
 	integer mp_local_cbsp_port := 15000;
 	integer mp_local_sbcap_port := 16000;
+	/* Must match osmo-cbc.cfg: */
+	BSC_modulepar_cfgs mp_bsc_cfg := {
+		{ tcp_is_client := true },
+		{ tcp_is_client := false }
+	};
+	MME_modulepar_cfgs mp_mme_cfg := {
+		{ sctp_is_client := true },
+		{ sctp_is_client := false }
+	};
 };
 
-const integer MAX_BSC := 3;
-const integer MAX_MME := 3;
-
 type component test_CT extends CBSP_Adapter_CT, http_CT {
 	timer g_Tguard := 60.0;
 	var integer g_num_bsc;
@@ -53,6 +73,8 @@
 	var MME_ConnHdlr g_vc_conn_MME[MAX_MME];
 	var BSC_ConnHdlrPars g_pars_BSC[MAX_BSC];
 	var MME_ConnHdlrPars g_pars_MME[MAX_MME];
+	port BSC_ConnHdlr_Coord_PT COORD_BSC[MAX_BSC];
+	port MME_ConnHdlr_Coord_PT COORD_MME[MAX_BSC];
 };
 
 private function f_shutdown_helper() runs on test_CT {
@@ -84,12 +106,16 @@
 private function f_BSC_ConnHdlr_start_fn_void() runs on BSC_ConnHdlr {
 	log("Default start_fn() function called!");
 }
-private function f_init_pars_bsc(integer bsc_cbsp_port, charstring cbc_host, integer cbc_cbsp_port)
+private function f_init_pars_bsc(charstring bsc_host, integer bsc_cbsp_port,
+				 charstring cbc_host, integer cbc_cbsp_port,
+				 boolean tcp_is_client)
 		runs on test_CT return BSC_ConnHdlrPars {
 	var BSC_ConnHdlrPars pars := {
+		bsc_host := bsc_host,
 		bsc_cbsp_port := bsc_cbsp_port,
 		cbc_host := cbc_host,
 		cbc_cbsp_port := cbc_cbsp_port,
+		tcp_is_client := tcp_is_client,
 		start_fn := refers(f_BSC_ConnHdlr_start_fn_void),
 		exp_cbs_msg := omit,
 		cell_list_success := omit
@@ -101,7 +127,10 @@
 	var BSC_ConnHdlr vc_conn;
 	id := id & "-BSC" & int2str(idx);
 	vc_conn := BSC_ConnHdlr.create(id) alive;
-	g_pars_BSC[idx] := f_init_pars_bsc(mp_local_cbsp_port + idx, mp_cbc_host, mp_cbc_cbsp_port);
+	g_pars_BSC[idx] := f_init_pars_bsc(mp_local_host, mp_local_cbsp_port + idx,
+					   mp_cbc_host, mp_cbc_cbsp_port,
+					   mp_bsc_cfg[idx].tcp_is_client);
+	connect(self:COORD_BSC[idx], vc_conn:COORD);
 	return vc_conn;
 }
 
@@ -118,12 +147,16 @@
 private function f_MME_ConnHdlr_start_fn_void() runs on MME_ConnHdlr {
 	log("Default start_fn() function called!");
 }
-private function f_init_pars_mme(integer mme_sbcap_port, charstring cbc_host, integer cbc_sbcap_port)
+private function f_init_pars_mme(charstring mme_host, integer mme_sbcap_port,
+				 charstring cbc_host, integer cbc_sbcap_port,
+				 boolean sctp_is_client)
 		runs on test_CT return MME_ConnHdlrPars {
 	var MME_ConnHdlrPars pars := {
+		mme_host := mme_host,
 		mme_sbcap_port := mme_sbcap_port,
 		cbc_host := cbc_host,
 		cbc_sbcap_port := cbc_sbcap_port,
+		sctp_is_client := sctp_is_client,
 		start_fn := refers(f_MME_ConnHdlr_start_fn_void),
 		exp_cbs_msg := omit,
 		write_replace_warning_ind_cause := omit,
@@ -136,7 +169,10 @@
 	var MME_ConnHdlr vc_conn;
 	id := id & "-MME" & int2str(idx);
 	vc_conn := MME_ConnHdlr.create(id) alive;
-	g_pars_MME[idx] := f_init_pars_mme(mp_local_sbcap_port + idx, mp_cbc_host, mp_cbc_sbcap_port);
+	g_pars_MME[idx] := f_init_pars_mme(mp_local_host, mp_local_sbcap_port + idx,
+					   mp_cbc_host, mp_cbc_sbcap_port,
+					   mp_mme_cfg[idx].sctp_is_client);
+	connect(self:COORD_MME[idx], vc_conn:COORD);
 	return vc_conn;
 }
 
@@ -172,7 +208,14 @@
 	for (var integer i := 0; i < g_num_mme; i := i + 1) {
 		f_start_mme(i, testcasename(), g_pars_MME[i]);
 	}
-	f_sleep(2.0); /* wait all conns connected */
+
+	/* Now wait for conns to be ready: */
+	for (var integer i := 0; i < g_num_bsc; i := i + 1) {
+		COORD_BSC[i].receive(COORD_MSG_CONNECTED);
+	}
+	for (var integer i := 0; i < g_num_mme; i := i + 1) {
+		COORD_MME[i].receive(COORD_MSG_CONNECTED);
+	}
 }
 
 /* test whether or not we receive a valid KEEP-ALIVE from the CBC */
@@ -483,6 +526,56 @@
 	f_shutdown_helper();
 }
 
+/* Create and delete message with BSC acting as TCP server */
+testcase TC_ecbe_create_delete_bsc_server() runs on test_CT {
+	var template (value) BSSMAP_FIELD_CellIdentificationList cell_list_success;
+	var template (value) CBS_Message msg := t_CBSmsg(46, 16752);
+
+	/* The 2nd BSC is the one configured as server, but we only want to test
+	 * that one, so initialize both but copy over config of the 2nd one to
+	 * the first one, to start only one BSC: */
+	f_init(num_bsc := 2);
+	g_num_bsc := 1;
+	g_pars_BSC[0] := g_pars_BSC[1];
+
+	cell_list_success := ts_BSSMAP_CIL_CI({
+		ts_BSSMAP_CI_CI(50001),
+		ts_BSSMAP_CI_CI(50002),
+		ts_BSSMAP_CI_CI(50003)
+	});
+	g_pars_BSC[0].start_fn := refers(f_bsc_create_and_delete);
+	g_pars_BSC[0].exp_cbs_msg := valueof(msg);
+	g_pars_BSC[0].cell_list_success := valueof(cell_list_success);
+	f_start();
+	f_create_and_delete(valueof(msg));
+	f_shutdown_helper();
+}
+
+/* Create and delete message with MME acting as SCTP server */
+testcase TC_ecbe_create_delete_mme_server() runs on test_CT {
+	var template (value) CellId_Broadcast_List bcast_cell_id_li;
+	var template (value) CBS_Message msg := t_CBSmsg(48, 16752);
+
+	/* The 2nd MME is the one configured as server, but we only want to test
+	 * that one, so initialize both but copy over config of the 2nd one to
+	 * the first one, to start only one MME: */
+	f_init(num_bsc := 0, num_mme := 2);
+	g_num_mme := 1;
+	g_pars_MME[0] := g_pars_MME[1];
+
+	bcast_cell_id_li := {
+		ts_SBCAP_CellId_Broadcast_List_Item(ts_SBCAP_ECGI(f_enc_mcc_mnc('901'H, '70'H), 1234)),
+		ts_SBCAP_CellId_Broadcast_List_Item(ts_SBCAP_ECGI(f_enc_mcc_mnc('901'H, '70'H), 5678))
+	};
+	g_pars_MME[0].start_fn := refers(f_mme_create_and_delete);
+	g_pars_MME[0].exp_cbs_msg := valueof(msg);
+	g_pars_MME[0].write_replace_warning_ind_cause := SBC_AP_Cause_message_accepted;
+	g_pars_MME[0].bcast_cell_id_list := valueof(bcast_cell_id_li);
+	f_start();
+	f_create_and_delete(valueof(msg));
+	f_shutdown_helper();
+}
+
 control {
 	execute( TC_rx_keepalive() );
 	execute( TC_rx_keepalive_timeout() );
@@ -493,6 +586,8 @@
 	execute( TC_ecbe_create_delete_lai() );
 	execute( TC_ecbe_create_delete_mme_indication() );
 	execute( TC_ecbe_create_delete_bsc_and_mme() );
+	execute( TC_ecbe_create_delete_bsc_server() );
+	execute( TC_ecbe_create_delete_mme_server() );
 }
 
 }
diff --git a/cbc/CBS_Message.ttcn b/cbc/CBS_Message.ttcn
index 6f77a1b..1ce6935 100644
--- a/cbc/CBS_Message.ttcn
+++ b/cbc/CBS_Message.ttcn
@@ -17,6 +17,10 @@
 
 import from CBSP_Types all;
 
+/* Messages used for coordination between handlers and test_CT: */
+const charstring COORD_MSG_CONNECTED := "COORD_MSG_CONNECTED";
+
+
 type record CBS_Message {
 	uint16_t msg_id,
 	uint16_t ser_nr,
diff --git a/cbc/MME_ConnectionHandler.ttcn b/cbc/MME_ConnectionHandler.ttcn
index fec358c..ed0502e 100644
--- a/cbc/MME_ConnectionHandler.ttcn
+++ b/cbc/MME_ConnectionHandler.ttcn
@@ -25,15 +25,23 @@
 
 type function void_fn() runs on MME_ConnHdlr;
 
+/* Coordinate with test_CT: */
+type port MME_ConnHdlr_Coord_PT message {
+	inout charstring;
+} with { extension "internal" };
+
 /* this component represents a single subscriber connection */
 type component MME_ConnHdlr extends SBC_AP_Adapter_CT {
 	var MME_ConnHdlrPars g_pars;
+	port MME_ConnHdlr_Coord_PT COORD;
 }
 
 type record MME_ConnHdlrPars {
+	charstring mme_host,
 	integer mme_sbcap_port,
 	charstring cbc_host,
 	integer cbc_sbcap_port,
+	boolean sctp_is_client,
 	void_fn start_fn,
 	CBS_Message exp_cbs_msg optional,
 	SBC_AP_Cause write_replace_warning_ind_cause optional,
@@ -42,7 +50,14 @@
 
 function f_MME_ConnHdlr_main(charstring id, MME_ConnHdlrPars pars) runs on MME_ConnHdlr {
 	g_pars := pars;
-	SBC_AP_Adapter.f_connect(g_pars.cbc_host, g_pars.cbc_sbcap_port, "", g_pars.mme_sbcap_port);
+	if (g_pars.sctp_is_client) {
+		SBC_AP_Adapter.f_connect(g_pars.cbc_host, g_pars.cbc_sbcap_port,
+					 g_pars.mme_host, g_pars.mme_sbcap_port);
+	} else {
+		SBC_AP_Adapter.f_bind(g_pars.mme_host, g_pars.mme_sbcap_port);
+		SBC_AP_Adapter.f_wait_client_connect();
+	}
+	COORD.send(COORD_MSG_CONNECTED);
 	g_pars.start_fn.apply();
 }
 
diff --git a/library/CBSP_Adapter.ttcn b/library/CBSP_Adapter.ttcn
index de89d58..278a2f2 100644
--- a/library/CBSP_Adapter.ttcn
+++ b/library/CBSP_Adapter.ttcn
@@ -77,6 +77,16 @@
 	f_set_tcp_segmentation(idx);
 }
 
+function f_wait_client_connect(integer idx := 0) runs on CBSP_Adapter_CT {
+	var IPL4asp_Types.PortEvent rx_event;
+	CBSP[idx].receive(IPL4asp_Types.PortEvent:{connOpened:=?}) -> value rx_event {
+		log("Connection from ", rx_event.connOpened.remName, ":", rx_event.connOpened.remPort);
+		/* we want to store the client's connId, not the 'bind socket' one */
+		g_cbsp_conn_id[idx] := rx_event.connOpened.connId;
+	}
+	f_set_tcp_segmentation(idx);
+}
+
 function f_cbsp_send(template (value) CBSP_PDU pdu, integer idx := 0) runs on CBSP_Adapter_CT {
 	CBSP[idx].send(ts_CBSP_Send(g_cbsp_conn_id[idx], pdu));
 }
diff --git a/library/sbcap/SBC_AP_Adapter.ttcn b/library/sbcap/SBC_AP_Adapter.ttcn
index 3217c5e..515a723 100644
--- a/library/sbcap/SBC_AP_Adapter.ttcn
+++ b/library/sbcap/SBC_AP_Adapter.ttcn
@@ -77,6 +77,23 @@
 	g_SBC_AP_conn_id[idx] := res.connId;
 }
 
+function f_wait_client_connect(integer idx := 0) runs on SBC_AP_Adapter_CT {
+	var IPL4asp_Types.PortEvent rx_event;
+	SBC_AP[idx].receive(IPL4asp_Types.PortEvent:{connOpened:=?}) -> value rx_event {
+		log("Connection from ", rx_event.connOpened.remName, ":", rx_event.connOpened.remPort);
+		/* we want to store the client's connId, not the 'bind socket' one */
+		g_SBC_AP_conn_id[idx] := rx_event.connOpened.connId;
+	}
+	timer Tcommup := 10.0;
+	Tcommup.start;
+	alt {
+	[] SBC_AP[idx].receive(tr_SctpAssocChange_COMM_UP(g_SBC_AP_conn_id[idx])) {}
+	[] Tcommup.timeout {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting SCTP_COMM_UP");
+	}
+	}
+}
+
 function f_SBC_AP_send(template (value) SBC_AP_PDU pdu, integer idx := 0) runs on SBC_AP_Adapter_CT {
 	SBC_AP[idx].send(ts_SBC_AP_Send(g_SBC_AP_conn_id[idx], pdu));
 }