bsc: Add testscase & infra to validate Osmux support BTS<->BSC

New TC_assignment_osmux_bts is added which tests Osmux used only
BTS<->BSC.
Existing TC_assignment_osmux is renamed to TC_assignment_osmux_cn,
and a new TC_assignment_osmux is added which tests using Osmux on both
sides (towards BTS and CN).

Related: SYS#5987
Change-Id: I6e82eb9d995de988b812001e1c4cf6923509de66
diff --git a/bsc/BSC_Tests.ttcn b/bsc/BSC_Tests.ttcn
index 46fb952..d4a7b20 100644
--- a/bsc/BSC_Tests.ttcn
+++ b/bsc/BSC_Tests.ttcn
@@ -636,6 +636,7 @@
 
 	/* Osmux is enabled through VTY */
 	var boolean g_osmux_enabled_cn := false;
+	var boolean g_osmux_enabled_bts := false;
 
 	/*Configure T(tias) over VTY, seconds */
 	var integer g_bsc_sccp_timer_ias :=  7 * 60;
@@ -1270,6 +1271,15 @@
 	}
 	/* wait until BSC tells us "connected" */
 	f_wait_oml(bts_idx, "connected", 5.0);
+
+	/* Set up BTS with VTY commands: */
+	f_vty_enter_cfg_bts(BSCVTY, bts_idx);
+	if (g_osmux_enabled_bts) {
+		f_vty_transceive(BSCVTY, "osmux on");
+	} else {
+		f_vty_transceive(BSCVTY, "osmux off");
+	}
+	f_vty_transceive(BSCVTY, "end");
 }
 
 function f_init_bts_and_check_sysinfo(integer bts_idx := 0,
@@ -1744,18 +1754,26 @@
 }
 
 /* generate an assignment complete template for either AoIP or SCCPlite */
-function f_gen_exp_compl(boolean expect_osmux := false, integer bssap_idx := 0) return template PDU_BSSAP {
+function f_gen_exp_compl(integer bssap_idx := 0)
+runs on MSC_ConnHdlr return template PDU_BSSAP {
 	var template PDU_BSSAP exp_compl;
-	var BSSMAP_IE_Osmo_OsmuxCID osmux_cid := valueof(ts_OsmuxCID(0));
 	if (mp_bssap_cfg[bssap_idx].transport == BSSAP_TRANSPORT_AoIP) {
-		if (expect_osmux) {
-			exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, osmux_cid);
-		} else {
-			exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, omit);
+		var template BSSMAP_IE_Osmo_OsmuxCID exp_osmux_cid := omit;
+		if (g_pars.use_osmux_cn) {
+			var template (present) INT1 exp_cid := ?;
+			if (isbound(g_media.mgcp_conn[0].local_osmux_cid) and isbound(g_media.mgcp_conn[1].local_osmux_cid)) {
+				exp_cid := (g_media.mgcp_conn[0].local_osmux_cid, g_media.mgcp_conn[1].local_osmux_cid);
+			} else if (isbound(g_media.mgcp_conn[0].local_osmux_cid)) {
+				exp_cid := g_media.mgcp_conn[0].local_osmux_cid;
+			} else if (isbound(g_media.mgcp_conn[1].local_osmux_cid)) {
+				exp_cid := g_media.mgcp_conn[1].local_osmux_cid;
+			}
+			exp_osmux_cid := tr_OsmuxCID(exp_cid);
 		}
+		exp_compl := tr_BSSMAP_AssignmentComplete(omit, ?, exp_osmux_cid);
 	} else {
 		/* CIC is optional "*" as the MSC allocated it */
-		exp_compl := tr_BSSMAP_AssignmentComplete(*, omit);
+		exp_compl := tr_BSSMAP_AssignmentComplete(*, omit, omit);
 	}
 	return exp_compl;
 }
@@ -4292,7 +4310,7 @@
 
 private function f_assignment_codec(charstring id, boolean do_perform_clear := true) runs on MSC_ConnHdlr {
 	var PDU_BSSAP ass_cmd := f_gen_ass_req(g_pars.use_osmux_cn);
-	var template PDU_BSSAP exp_compl := f_gen_exp_compl(g_pars.use_osmux_cn);
+	var template PDU_BSSAP exp_compl := f_gen_exp_compl();
 
 	/* puzzle together the ASSIGNMENT REQ for given codec[s] */
 	if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) {
@@ -5226,7 +5244,8 @@
 	f_shutdown_helper();
 }
 
-testcase TC_assignment_osmux() runs on test_CT {
+/* Test Osmux setup BSC<->MSC */
+testcase TC_assignment_osmux_cn() runs on test_CT {
 	var TestHdlrParams pars := f_gen_test_hdlr_pars();
 	var MSC_ConnHdlr vc_conn;
 
@@ -5256,6 +5275,54 @@
 	f_shutdown_helper();
 }
 
+/* Test Osmux setup BTS<->BSC */
+testcase TC_assignment_osmux_bts() runs on test_CT {
+	var TestHdlrParams pars := f_gen_test_hdlr_pars();
+	var MSC_ConnHdlr vc_conn;
+
+	pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H}));
+	pars.ass_codec_list.codecElements[0].s0_7 := '00000100'B; /* 5,90k */
+	pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B;
+	pars.expect_mr_conf_ie := mr_conf_amr_5_90;
+	pars.use_osmux_bts := true;
+
+	g_osmux_enabled_bts := true;
+	f_init(1, true);
+	f_sleep(1.0);
+	f_vty_amr_start_mode_set(false, "1");
+
+	vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars);
+	vc_conn.done;
+
+	f_vty_amr_start_mode_restore(false);
+	f_shutdown_helper();
+}
+
+/* Test Osmux setup BTS<->BSC<->MSC */
+testcase TC_assignment_osmux() runs on test_CT {
+	var TestHdlrParams pars := f_gen_test_hdlr_pars();
+	var MSC_ConnHdlr vc_conn;
+
+	pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecAMR_H}));
+	pars.ass_codec_list.codecElements[0].s0_7 := '00000100'B; /* 5,90k */
+	pars.ass_codec_list.codecElements[0].s8_15 := '00000111'B;
+	pars.expect_mr_conf_ie := mr_conf_amr_5_90;
+	pars.use_osmux_cn := true;
+	pars.use_osmux_bts := true;
+
+	g_osmux_enabled_cn := true;
+	g_osmux_enabled_bts := true;
+	f_init(1, true);
+	f_sleep(1.0);
+	f_vty_amr_start_mode_set(false, "1");
+
+	vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars);
+	vc_conn.done;
+
+	f_vty_amr_start_mode_restore(false);
+	f_shutdown_helper();
+}
+
 /* test the procedure of the MSC requesting a Classmark Update:
  * a) BSSMAP Classmark Request should result in RR CLASSMARK ENQUIRY,
  * b) L3 RR CLASSMARK CHANGE should result in BSSMAP CLASSMARK UPDATE */
@@ -10516,7 +10583,7 @@
 
 private function f_TC_refuse_mode_modif_to_vamos(charstring id) runs on MSC_ConnHdlr {
 	var PDU_BSSAP ass_cmd := f_gen_ass_req(g_pars.use_osmux_cn);
-	var template PDU_BSSAP exp_compl := f_gen_exp_compl(g_pars.use_osmux_cn);
+	var template PDU_BSSAP exp_compl := f_gen_exp_compl();
 
 	/* puzzle together the ASSIGNMENT REQ for given codec[s] */
 	if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) {
@@ -11833,6 +11900,8 @@
 	execute( TC_assignment_sdcch_exhausted_req_voice_tch_forbidden() );
 
 	execute( TC_assignment_osmux() );
+	execute( TC_assignment_osmux_cn() );
+	execute( TC_assignment_osmux_bts() );
 
 	/* RLL Establish Indication on inactive DCHAN / SAPI */
 	execute( TC_rll_est_ind_inact_lchan() );
diff --git a/bsc/BSC_Tests_VAMOS.ttcn b/bsc/BSC_Tests_VAMOS.ttcn
index 131c2d4..cd9ffd0 100644
--- a/bsc/BSC_Tests_VAMOS.ttcn
+++ b/bsc/BSC_Tests_VAMOS.ttcn
@@ -299,7 +299,7 @@
 
 private function f_est_lchan_and_mode_modify_to_vamos() runs on MSC_ConnHdlr {
 	var PDU_BSSAP ass_cmd := f_gen_ass_req(g_pars.use_osmux_cn);
-	var template PDU_BSSAP exp_compl := f_gen_exp_compl(g_pars.use_osmux_cn);
+	var template PDU_BSSAP exp_compl := f_gen_exp_compl();
 
 	/* puzzle together the ASSIGNMENT REQ for given codec[s] */
 	if (mp_bssap_cfg[0].transport == BSSAP_TRANSPORT_AoIP) {
diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn
index d07c912..8524f4b 100644
--- a/bsc/MSC_ConnectionHandler.ttcn
+++ b/bsc/MSC_ConnectionHandler.ttcn
@@ -52,6 +52,9 @@
  * Media related handling
  ***********************************************************************/
 
+/* TODO: import OSMUX_Types.ttcn */
+type INT1 OsmuxCID	(0 .. 255);
+
 /* Get the matching payload type for a specified BSSAP codec type
  * (see also: BSSAP_Types.ttcn */
 private function f_get_mgcp_pt(BSSMAP_FIELD_CodecType codecType) return SDP_FIELD_PayloadType {
@@ -88,7 +91,9 @@
 	integer ptime,			/* 20 */
 	uint7_t rtp_pt,			/* RTP Payload Type */
 	HostPort mgw,			/* MGW side */
-	HostPort peer			/* CA side */
+	HostPort peer,			/* CA side */
+	OsmuxCID local_osmux_cid optional,
+	OsmuxCID remote_osmux_cid optional
 };
 
 /* BTS media state */
@@ -98,7 +103,9 @@
 	uint16_t conn_id,
 	uint7_t rtp_pt,
 	HostPort bts,
-	HostPort peer
+	HostPort peer,
+	OsmuxCID local_osmux_cid optional,
+	OsmuxCID remote_osmux_cid optional
 };
 
 type record MediaState {
@@ -119,7 +126,9 @@
 			host := bts,
 			port_nr := 9000 + nr*2
 		},
-		peer := -
+		peer := -,
+		local_osmux_cid := nr,
+		remote_osmux_cid := omit
 	}
 
 	g_media.bts1 := {
@@ -131,7 +140,9 @@
 			host := bts, /* FIXME */
 			port_nr := 9000 + nr*2
 		},
-		peer := -
+		peer := -,
+		local_osmux_cid := nr,
+		remote_osmux_cid := omit
 	}
 
 	g_media.mgcp_ep := "rtpbridge/" & int2str(nr) & "@mgw";
@@ -146,6 +157,8 @@
 		g_media.mgcp_conn[i].crcx_seen_exp := 0;
 		g_media.mgcp_conn[i].mdcx_seen_exp := 0;
 		g_media.mgcp_conn[i].conn_id := f_mgcp_alloc_conn_id();
+		g_media.mgcp_conn[i].local_osmux_cid := i;
+		g_media.mgcp_conn[i].remote_osmux_cid := omit;
 	}
 
 	g_media.mgcp_conn[0].mgw := {
@@ -199,10 +212,23 @@
 		if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) {
 			g_media.bts.rtp_pt := ie.ipa_rtp_pt2;
 		}
+		if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) {
+			if (not g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX with Osmux CID IE");
+			}
+			g_media.bts.remote_osmux_cid := ie.osmux_cid.cid;
+		} else {
+			if (g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX without Osmux CID IE");
+			}
+			g_media.bts.local_osmux_cid := omit;
+			g_media.bts.remote_osmux_cid := omit;
+		}
 		rsl_pt.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts.conn_id,
 						f_inet_addr(g_media.bts.bts.host),
 						g_media.bts.bts.port_nr,
-						g_media.bts.rtp_pt));
+						g_media.bts.rtp_pt,
+						g_media.bts.local_osmux_cid));
 		g_media.bts.ipa_crcx_seen := true;
 		repeat;
 		}
@@ -224,10 +250,23 @@
 		if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) {
 			g_media.bts.rtp_pt := ie.ipa_rtp_pt2;
 		}
+		if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) {
+			if (not g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX with Osmux CID IE");
+			}
+			g_media.bts.remote_osmux_cid := ie.osmux_cid.cid;
+		} else {
+			if (g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX without Osmux CID IE");
+			}
+			g_media.bts.local_osmux_cid := omit;
+			g_media.bts.remote_osmux_cid := omit;
+		}
 		rsl_pt.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts.conn_id,
 						f_inet_addr(g_media.bts.peer.host),
 						g_media.bts.peer.port_nr,
-						g_media.bts.rtp_pt));
+						g_media.bts.rtp_pt,
+						g_media.bts.local_osmux_cid));
 		g_media.bts.ipa_mdcx_seen := true;
 		repeat;
 		}
@@ -241,10 +280,23 @@
 		if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) {
 			g_media.bts1.rtp_pt := ie.ipa_rtp_pt2;
 		}
+		if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) {
+			if (not g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX with Osmux CID IE");
+			}
+			g_media.bts.remote_osmux_cid := ie.osmux_cid.cid;
+		} else {
+			if (g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC CRCX without Osmux CID IE");
+			}
+			g_media.bts.local_osmux_cid := omit;
+			g_media.bts.remote_osmux_cid := omit;
+		}
 		rsl_pt_ho_target.send(ts_RSL_IPA_CRCX_ACK(g_chan_nr, g_media.bts1.conn_id,
 						f_inet_addr(g_media.bts1.bts.host),
 						g_media.bts1.bts.port_nr,
-						g_media.bts1.rtp_pt));
+						g_media.bts1.rtp_pt,
+						g_media.bts.local_osmux_cid));
 		g_media.bts1.ipa_crcx_seen := true;
 		repeat;
 		}
@@ -267,10 +319,23 @@
 		if (f_rsl_find_ie(rsl, RSL_IE_IPAC_RTP_PAYLOAD2, ie)) {
 			g_media.bts1.rtp_pt := ie.ipa_rtp_pt2;
 		}
+		if (f_rsl_find_ie(rsl, RSL_IE_OSMO_OSMUX_CID, ie)) {
+			if (not g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX with Osmux CID IE");
+			}
+			g_media.bts.remote_osmux_cid := ie.osmux_cid.cid;
+		} else {
+			if (g_pars.use_osmux_bts) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Rx unexpected IPAC MDCX without Osmux CID IE");
+			}
+			g_media.bts.local_osmux_cid := omit;
+			g_media.bts.remote_osmux_cid := omit;
+		}
 		rsl_pt_ho_target.send(ts_RSL_IPA_MDCX_ACK(g_chan_nr, g_media.bts1.conn_id,
 						f_inet_addr(g_media.bts1.peer.host),
 						g_media.bts1.peer.port_nr,
-						g_media.bts1.rtp_pt));
+						g_media.bts1.rtp_pt,
+						g_media.bts.local_osmux_cid));
 		g_media.bts1.ipa_mdcx_seen := true;
 		repeat;
 		}
@@ -280,10 +345,10 @@
 
 function f_rx_crcx(MgcpCommand mgcp_cmd)
 	 runs on MSC_ConnHdlr return template MgcpResponse {
-	var MgcpOsmuxCID osmux_cid;
 	var SDP_Message sdp;
 	var integer cid := f_get_free_mgcp_conn();
 	var charstring local_rtp_addr;
+	var MgcpOsmuxCID osmux_cid;
 
 	if (g_pars.media_mgw_offer_ipv6 == true) {
 		local_rtp_addr := host_mgw_rtp_v6; /* Use IPv6 by default if no remote addr is provided by client */
@@ -318,9 +383,13 @@
 						int2str(mgcp_conn.sample_rate))),
 			valueof(ts_SDP_ptime(mgcp_conn.ptime)) } ));
 	var template MgcpResponse mgcp_resp;
-	if (g_pars.use_osmux_cn and f_MgcpCmd_contains_par(mgcp_cmd, "X-OSMUX")) {
+	if ((g_pars.use_osmux_cn or g_pars.use_osmux_bts) and
+	    f_MgcpCmd_contains_par(mgcp_cmd, "X-OSMUX")) {
 		osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd);
-		mgcp_resp := ts_CRCX_ACK_osmux(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, osmux_cid, sdp);
+		if (osmux_cid != -1) {
+			mgcp_conn.remote_osmux_cid := osmux_cid;
+		}
+		mgcp_resp := ts_CRCX_ACK_osmux(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, mgcp_conn.local_osmux_cid, sdp);
 	} else {
 		mgcp_resp := ts_CRCX_ACK(mgcp_cmd.line.trans_id, mgcp_conn.conn_id, sdp);
 	}
@@ -675,6 +744,7 @@
 	boolean         exp_ms_power_params,
 	boolean		aoip,
 	boolean		use_osmux_cn,
+	boolean		use_osmux_bts,
 	charstring	host_aoip_tla,
 	TestHdlrParamsMSCPool mscpool,
 	boolean		media_mgw_offer_ipv6,
@@ -713,6 +783,7 @@
 	exp_ms_power_params := false,
 	aoip := true,
 	use_osmux_cn := false,
+	use_osmux_bts := false,
 	host_aoip_tla := "1.2.3.4",
 	mscpool := {
 		bssap_idx := 0,
diff --git a/bsc/expected-results.xml b/bsc/expected-results.xml
index 23217ba..223800b 100644
--- a/bsc/expected-results.xml
+++ b/bsc/expected-results.xml
@@ -106,6 +106,7 @@
   <testcase classname='BSC_Tests' name='TC_assignment_sdcch_exhausted_req_signalling_tch_forbidden' time='MASKED'/>
   <testcase classname='BSC_Tests' name='TC_assignment_sdcch_exhausted_req_voice_tch_forbidden' time='MASKED'/>
   <testcase classname='BSC_Tests' name='TC_assignment_osmux' time='MASKED'/>
+  <testcase classname='BSC_Tests' name='TC_assignment_osmux_bts' time='MASKED'/>
   <testcase classname='BSC_Tests' name='TC_rll_est_ind_inact_lchan' time='MASKED'/>
   <testcase classname='BSC_Tests' name='TC_rll_est_ind_inval_sapi1' time='MASKED'/>
   <testcase classname='BSC_Tests' name='TC_rll_est_ind_inval_sapi3' time='MASKED'/>
diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn
index 633b060..26f7ed9 100644
--- a/library/BSSMAP_Templates.ttcn
+++ b/library/BSSMAP_Templates.ttcn
@@ -614,6 +614,10 @@
 	codecElements := valueof(elem)
 }
 
+template (present) BSSMAP_IE_Osmo_OsmuxCID tr_OsmuxCID(template (present) INT1 cid := ?) := {
+	elementIdentifier := 'F1'O,
+	osmuxCID := cid
+}
 template (value) BSSMAP_IE_Osmo_OsmuxCID ts_OsmuxCID(INT1 cid) := {
 	elementIdentifier := 'F1'O,
 	osmuxCID := cid
diff --git a/library/RSL_Types.ttcn b/library/RSL_Types.ttcn
index 6c45ca9..3acd619 100644
--- a/library/RSL_Types.ttcn
+++ b/library/RSL_Types.ttcn
@@ -220,9 +220,11 @@
 		RSL_IE_TFO_STATUS		('00111011'B),
 		RSL_IE_LLP_APDU			('00111100'B),
 
+		/* Osmocom extensions */
 		RSL_IE_OSMO_REP_ACCH_CAP	('01100000'B),
 		RSL_IE_OSMO_TRAINING_SEQUENCE	('01100001'B),
 		RSL_IE_OSMO_TOP_ACCH_CAP	('01100010'B),
+		RSL_IE_OSMO_OSMUX_CID		('01100011'B),
 
 		/* ip.access */
 		RSL_IE_IPAC_SRTP_CONFIG		('11100000'B),
@@ -961,6 +963,23 @@
 		overpower_db := overpower
 	};
 
+	type record RSL_IE_OSMO_Osmux_CID {
+		uint8_t			len,
+		uint8_t			cid
+	} with { variant (len) "LENGTHTO(cid)" }
+
+	template (present) RSL_IE_OSMO_Osmux_CID
+	tr_RSL_IE_OSMO_Osmux_CID(template (present) uint8_t osmux_cid := ?) := {
+		len := ?, /* overwritten */
+		cid := osmux_cid
+	};
+	template (value) RSL_IE_OSMO_Osmux_CID
+	ts_RSL_IE_OSMO_Osmux_CID(template (value) uint8_t osmux_cid) := {
+		len := 0, /* overwritten */
+		cid := osmux_cid
+	};
+
+
 	/* union of all IE bodies */
 	type union RSL_IE_Body {
 		RslChannelNr		chan_nr,
@@ -1018,6 +1037,7 @@
 		RSL_IE_OSMO_TrainingSequence osmo_training_sequence,
 		RSL_IE_OSMO_RepAcchCap	rep_acch_cap,
 		RSL_IE_OSMO_TopAcchCap	top_acch_cap,
+		RSL_IE_OSMO_Osmux_CID	osmux_cid,
 
 		RSL_LV		other
 	}
@@ -1083,6 +1103,7 @@
 					osmo_training_sequence, iei = RSL_IE_OSMO_TRAINING_SEQUENCE;
 					rep_acch_cap, iei = RSL_IE_OSMO_REP_ACCH_CAP;
 					top_acch_cap, iei = RSL_IE_OSMO_TOP_ACCH_CAP;
+					osmux_cid, iei = RSL_IE_OSMO_OSMUX_CID;
 
 					other, OTHERWISE;
 		)" };
@@ -2184,20 +2205,28 @@
 		ies := f_ts_RSL_IPA_CRCX_IEs(chan_nr, remote_ip, remote_port)
 	}
 
-
-	template (value) RSL_Message ts_RSL_IPA_CRCX_ACK(template (value) RslChannelNr chan_nr,
-							 uint16_t ipa_conn_id, OCT4 local_ip,
-							 uint16_t local_port, uint7_t rtp_pt2) := {
-		msg_disc := ts_RSL_MsgDisc(RSL_MDISC_IPACCESS, false),
-		msg_type := RSL_MT_IPAC_CRCX_ACK,
-		ies := {
-			t_RSL_IE(RSL_IE_CHAN_NR, RSL_IE_Body:{chan_nr := chan_nr}),
-			t_RSL_IE(RSL_IE_IPAC_CONN_ID, RSL_IE_Body:{ipa_conn_id := ipa_conn_id}),
-			t_RSL_IE(RSL_IE_IPAC_LOCAL_IP, RSL_IE_Body:{ipa_local_ip := local_ip}),
-			t_RSL_IE(RSL_IE_IPAC_LOCAL_PORT, RSL_IE_Body:{ipa_local_port := local_port}),
-			t_RSL_IE(RSL_IE_IPAC_RTP_PAYLOAD2, RSL_IE_Body:{ipa_rtp_pt2 := rtp_pt2})
+	function ts_RSL_IPA_CRCX_ACK(template (value) RslChannelNr chan_nr,
+				     uint16_t ipa_conn_id, OCT4 local_ip,
+				     uint16_t local_port, uint7_t rtp_pt2,
+				     template (omit) uint8_t osmux_cid := omit)
+	return template (value) RSL_Message {
+		var template (value) RSL_Message msg := {
+			msg_disc := ts_RSL_MsgDisc(RSL_MDISC_IPACCESS, false),
+			msg_type := RSL_MT_IPAC_CRCX_ACK,
+			ies := {
+				t_RSL_IE(RSL_IE_CHAN_NR, RSL_IE_Body:{chan_nr := chan_nr}),
+				t_RSL_IE(RSL_IE_IPAC_CONN_ID, RSL_IE_Body:{ipa_conn_id := ipa_conn_id}),
+				t_RSL_IE(RSL_IE_IPAC_LOCAL_IP, RSL_IE_Body:{ipa_local_ip := local_ip}),
+				t_RSL_IE(RSL_IE_IPAC_LOCAL_PORT, RSL_IE_Body:{ipa_local_port := local_port}),
+				t_RSL_IE(RSL_IE_IPAC_RTP_PAYLOAD2, RSL_IE_Body:{ipa_rtp_pt2 := rtp_pt2})
+			}
 		}
+		if (not istemplatekind(osmux_cid, "omit")) {
+			msg.ies[lengthof(msg.ies)] := t_RSL_IE(RSL_IE_OSMO_OSMUX_CID, RSL_IE_Body:{osmux_cid := ts_RSL_IE_OSMO_Osmux_CID(osmux_cid)});
+		}
+		return msg;
 	}
+
 	template RSL_Message tr_RSL_IPA_CRCX_ACK(template RslChannelNr chan_nr,
 						 template uint16_t ipa_conn_id,
 						 template OCT4 local_ip,
@@ -2258,22 +2287,30 @@
 		}
 	}
 
-	template (value) RSL_Message ts_RSL_IPA_MDCX_ACK(template (value) RslChannelNr chan_nr,
-							 uint16_t ipa_conn_id,
-							 OCT4 local_ip, uint16_t local_port,
-							 uint7_t rtp_pt2) := {
-		msg_disc := ts_RSL_MsgDisc(RSL_MDISC_IPACCESS, false),
-		msg_type := RSL_MT_IPAC_MDCX_ACK,
-		ies := {
-			t_RSL_IE(RSL_IE_CHAN_NR, RSL_IE_Body:{chan_nr := chan_nr}),
-			/* optional */
-			t_RSL_IE(RSL_IE_IPAC_CONN_ID, RSL_IE_Body:{ipa_conn_id := ipa_conn_id}),
-			t_RSL_IE(RSL_IE_IPAC_LOCAL_IP, RSL_IE_Body:{ipa_local_ip := local_ip}),
-			t_RSL_IE(RSL_IE_IPAC_LOCAL_PORT, RSL_IE_Body:{ipa_local_port := local_port}),
-			/* optional: RTP Payload Type */
-			t_RSL_IE(RSL_IE_IPAC_RTP_PAYLOAD2, RSL_IE_Body:{ipa_rtp_pt2 := rtp_pt2})
+	function ts_RSL_IPA_MDCX_ACK(template (value) RslChannelNr chan_nr,
+				     uint16_t ipa_conn_id,
+				     OCT4 local_ip, uint16_t local_port,
+				     uint7_t rtp_pt2,
+				     template (omit) uint8_t osmux_cid := omit)
+		return template (value) RSL_Message {
+			var template (value) RSL_Message msg := {
+				msg_disc := ts_RSL_MsgDisc(RSL_MDISC_IPACCESS, false),
+				msg_type := RSL_MT_IPAC_MDCX_ACK,
+				ies := {
+					t_RSL_IE(RSL_IE_CHAN_NR, RSL_IE_Body:{chan_nr := chan_nr}),
+					/* optional */
+					t_RSL_IE(RSL_IE_IPAC_CONN_ID, RSL_IE_Body:{ipa_conn_id := ipa_conn_id}),
+					t_RSL_IE(RSL_IE_IPAC_LOCAL_IP, RSL_IE_Body:{ipa_local_ip := local_ip}),
+					t_RSL_IE(RSL_IE_IPAC_LOCAL_PORT, RSL_IE_Body:{ipa_local_port := local_port}),
+					/* optional: RTP Payload Type */
+					t_RSL_IE(RSL_IE_IPAC_RTP_PAYLOAD2, RSL_IE_Body:{ipa_rtp_pt2 := rtp_pt2})
+				}
+			}
+			if (not istemplatekind(osmux_cid, "omit")) {
+				msg.ies[lengthof(msg.ies)] := t_RSL_IE(RSL_IE_OSMO_OSMUX_CID, RSL_IE_Body:{osmux_cid := ts_RSL_IE_OSMO_Osmux_CID(osmux_cid)});
+			}
+			return msg;
 		}
-	}
 	template RSL_Message tr_RSL_IPA_MDCX_ACK(template RslChannelNr chan_nr,
 						 template uint16_t ipa_conn_id,
 						 template OCT4 local_ip,