/* 5GC (55G Core) test suite in TTCN-3
 * (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 * All rights reserved.
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

module C5G_Tests {

import from General_Types all;
import from Native_Functions all;
import from IPL4asp_Types all;
import from Misc_Helpers all;

import from Osmocom_Types all;
import from GSM_Types all;

import from DNS_Helpers all;

import from Milenage_Functions all;

import from NGAP_PDU_Descriptions all;
import from NGAP_IEs all;
import from NGAP_PDU_Contents all;
import from NGAP_Constants all;

import from NGAP_Types all;
import from NGAP_Pixits all;
import from NGAP_Templates all;
import from NGAP_Functions all;
import from NGAP_Emulation all;

import from NAS_CommonTypeDefs all;
import from NAS_CommonTemplates all;
import from NG_NAS_Common all;
import from NG_NAS_TypeDefs all;
import from NG_NAS_MsgContainers all;
import from NG_NAS_Templates all;

import from NG_NAS_Osmo_Types all;
import from NG_NAS_Osmo_Templates all;
import from NG_NAS_Functions all;
import from NG_CryptoFunctions all;

import from GTPv1U_Emulation all;

import from ConnHdlr all;

modulepar {
	/* NG-C interface */
	charstring mp_5gc_ngap_ip := "127.0.0.1";
	integer mp_5gc_ngap_port := 38412;
	charstring mp_local_ngap_ip := "127.0.0.1";
	integer mp_local_ngap_port := 50000;
	GsmMcc mp_mcc := '999'H;
	GsmMnc mp_mnc := '70'H;
	HEX15n mp_imsi := '999700000000000'H;
	octetstring mp_usim_key := '762a2206fe0b4151ace403c86a11e479'O;
	octetstring mp_usim_opc := '3c6e0b8a9c15224a8228b9a98ca1531d'O;
	uint24_t mp_tac := 1;
	charstring mp_apn := "internet";
	charstring mp_local_gtpu_ip := "127.0.0.20";
	charstring mp_ping_hostname := "10.45.0.1";
	charstring mp_ping_hostname6 := "cafe::1";
	charstring mp_pcscf_hostname4 := "127.0.0.7";
	charstring mp_pcscf_hostname6 := "fd02:db8:18::7";

	charstring mp_run_prog_log_path := "/tmp";
	charstring mp_run_prog_as_user := "osmocom";
}

template (value) RunProgParams ts_RunProgParams(integer imsi_suffix) :=
{
	run_as_user := mp_run_prog_as_user,
	log_path_prefix := mp_run_prog_log_path,
	tun_netns_name := "tun" & int2str(imsi_suffix),
	tun_dev_name := "tun" & int2str(imsi_suffix),
	ping_hostname := mp_ping_hostname
}

template (value) PDUSessionParams ts_PDUSessionParams(template (value) PDUSessionID id,
						      template (value) RunProgParams run_prog_pars,
						      template (value) OCT4 ran_gtpu_teid) :=
{
	id := id,
	run_prog_pars := run_prog_pars,
	ran_gtpu_ip := mp_local_gtpu_ip,
	ran_gtpu_teid := ran_gtpu_teid,
	cn_gtpu_ip := omit,
	cn_gtpu_teid := omit,
	qos_rules := omit,
	ue_ip4 := omit,
	ue_ip6 := omit,
	ipv6_interface_id := omit
};

template (value) UeParams ts_UeParams(integer idx) :=
{
	idx := idx,
	imsi := f_concat_pad(lengthof(mp_imsi), substr(mp_imsi, 0, lengthof(mp_imsi) - 6), idx),
	imeisv := f_rnd_imeisv(),
	usim_key := mp_usim_key,
	usim_opc := mp_usim_opc,
	apn := mp_apn,
	ran_id := idx,
	amf_id := omit,
	guti := omit,
	pti := '00'O,
	sess_pars := ts_PDUSessionParams(id := 1,
					 run_prog_pars := ts_RunProgParams(idx),
					 ran_gtpu_teid := int2oct(idx + 1, 4))
}

type component MTC_CT {
	/* S1 intreface of emulated ENBs */
	var NGRANParams g_ngran_pars[NUM_NGRAN];
	var NGAP_Emulation_CT vc_NGAP[NUM_NGRAN];
	port NGAP_PT NGAP_UNIT[NUM_NGRAN];
	port NGAPEM_PROC_PT NGAP_PROC[NUM_NGRAN];

	var GTPv1U_Emulation_CT vc_GTP1U;
	port GTP1UEM_PT TEID0;

	timer g_Tguard := 30.0;
}

/* send incoming unit data messages (like reset) to global NGAP_UNIT port */
private function NGapForwardUnitdataCallback(NGAP_PDU msg)
runs on NGAP_Emulation_CT return template NGAP_PDU {
	NGAP_UNIT.send(msg);
	return omit;
}

private function f_init_one_ngran(integer num := 0) runs on MTC_CT {
	var charstring id := testcasename() & "-NGAP" & int2str(num);
	var NGAPOps ops := {
		create_cb := refers(NGAP_Emulation.ExpectedCreateCallback),
		unitdata_cb := refers(NGapForwardUnitdataCallback)
	};
	var NGAP_conn_parameters pars := {
		remote_ip := mp_5gc_ngap_ip,
		remote_sctp_port := mp_5gc_ngap_port,
		local_ip := mp_local_ngap_ip,
		local_sctp_port := mp_local_ngap_port + num,
		role := NG_NAS_ROLE_UE
	};
	var PLMNIdentity plmn_id := f_enc_mcc_mnc(mp_mcc, mp_mnc);
	var NGRANParams ngran_pars := {
		global_ngran_id := valueof(m_globalRANNodeID_globalGNB_ID(m_ie_globalGnbId(plmn_id, int2bit(num, 22)))),
		cell_identity := { nR_CGI := valueof(m_nR_CGI(plmn_id, int2bit(num, 36))) },
		supported_ta_list := {
			{
				tAC := int2oct(mp_tac, 3),
				broadcastPLMNList := {
					valueof(m_ie_broadcastPLMNItem(plmn_id, { m_sliceSupportItem(m_s_NSSAI('01'O)) }))
				},
				iE_Extensions := omit
			}
		}
	};

	g_ngran_pars[num] := ngran_pars;
	vc_NGAP[num] := NGAP_Emulation_CT.create(id);
	map(vc_NGAP[num]:NGAP, system:NGAP_CODEC_PT);
	connect(vc_NGAP[num]:NGAP_PROC, self:NGAP_PROC[num]);
	connect(vc_NGAP[num]:NGAP_UNIT, self:NGAP_UNIT[num]);
	vc_NGAP[num].start(NGAP_Emulation.main(ops, pars, id));
	NGAP_UNIT[num].receive(NGAPEM_Event:{up_down:=NGAPEM_EVENT_UP});
}

private function f_init_ngap(integer imsi_suffix := 0) runs on MTC_CT {
	var integer i;
	for (i := 0; i < NUM_NGRAN; i := i+1) {
		f_init_one_ngran(i);
	}
}

private function f_init_gtp1u() runs on MTC_CT {
	var Gtp1uEmulationCfg cfg := {
		gtpu_bind_ip := omit,  /* using gtpu daemon */
		gtpu_bind_port := omit,  /* using gtpu daemon */
		use_gtpu_daemon := true
	};

	vc_GTP1U := GTPv1U_Emulation_CT.create("GTP1U_EM");
	map(vc_GTP1U:GTP1U, system:GTP1U);
	connect(vc_GTP1U:TEID0, self:TEID0);
	vc_GTP1U.start(GTPv1U_Emulation.main(cfg));
}

private function f_init(integer imsi_suffix := 0, float t_guard := 30.0) runs on MTC_CT {
	/* start guard timer and activate it as default */
	g_Tguard.start(t_guard);
	activate(as_Tguard());

	f_init_gtp1u();
	f_init_ngap(imsi_suffix);
}

/* generate parameters for a connection handler */
private function f_init_pars(integer ue_idx := 0, integer c5g_idx := 0)
runs on MTC_CT return ConnHdlrPars {
	var ConnHdlrPars pars := {
		ngran_pars := g_ngran_pars,
		ue_pars := valueof(ts_UeParams(ue_idx)),
		c5g_idx := c5g_idx,
		kset_id := valueof(cs_NAS_KeySetIdentifier_lv(tsc_NasKsi_NoKey, '0'B)),
		exp_CoreNetworkAssistanceInformationForInactive := false
	};
	return pars;
}

private function f_start_handler_create(integer ngap_idx := 0)
runs on MTC_CT return ConnHdlr
{
	var ConnHdlr vc_conn;
	var charstring id := testcasename() & int2str(ngap_idx);

	vc_conn := ConnHdlr.create(id);
	/* NGAP part */
	connect(vc_conn:NGAP, vc_NGAP[ngap_idx]:NGAP_CLIENT);
	connect(vc_conn:NGAP_PROC, vc_NGAP[ngap_idx]:NGAP_PROC);

	/* GTPv1U */
	connect(vc_conn:GTP1U[0], vc_GTP1U:CLIENT);
	connect(vc_conn:GTP1U_PROC[0], vc_GTP1U:CLIENT_PROC);

	return vc_conn;
}

private function f_start_handler_run(ConnHdlr vc_conn, void_fn fn, ConnHdlrPars pars) runs on MTC_CT
{
	vc_conn.start(f_init_handler(fn, pars));
}

/* start a connection handler with given parameters */
private function f_start_handler_with_pars(void_fn fn, ConnHdlrPars pars, integer ngap_idx := 0)
runs on MTC_CT return ConnHdlr {
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_create(ngap_idx);
	f_start_handler_run(vc_conn, fn, pars);

	return vc_conn;
}

/* altstep for the global guard timer */
private altstep as_Tguard() runs on MTC_CT {
	[] g_Tguard.timeout {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Tguard timeout");
	}
}

private function f_init_handler(void_fn fn, ConnHdlrPars pars) runs on ConnHdlr {
	/* make parameters available via component variable */
	g_pars := pars;
	fn.apply();
}

private function f_ngap_setup(integer idx := 0, template (omit) NGAP_IEs.Cause cause := omit) runs on MTC_CT {
	var template (present) NGAP_IEs.Cause exp_cause := ?;
	var boolean exp_fail := false;
	timer T := 5.0;
	var template (value) NGAP_PDU tx_pdu;
	var template (present) NGAP_PDU exp_pdu;
	var NGAP_PDU rx_pdu;

	tx_pdu := m_ngap_initMsg(m_n2_NGSetupRequest(g_ngran_pars[idx].global_ngran_id,
						     g_ngran_pars[idx].supported_ta_list,
						     v32));

	if (not istemplatekind(cause, "omit")) {
		exp_fail := true;
		exp_cause := cause;
	}

	if (exp_fail) {
		exp_pdu := mw_ngap_unsuccMsg((mw_n2_NGSetupFailure(exp_cause),
					      f_mw_n2_NGSetupFailure(exp_cause, p_timeToWait := ?)));
	} else {
		exp_pdu := mw_ngap_succMsg(mw_n2_NGSetupResponse);
	}

	NGAP_UNIT[idx].send(tx_pdu);
	T.start;
	alt {
	[] NGAP_UNIT[idx].receive(exp_pdu) {
		setverdict(pass);
		}
	[] NGAP_UNIT[idx].receive(NGAP_PDU:?) -> value rx_pdu {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
					log2str("Received unexpected NGAP ", rx_pdu, " vs exp ", exp_pdu));
		}
	[] T.timeout {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for NGAP Setup result");
		}
	}
}

/* NG Setup procedure to 5GC using a Global gNB ID containing unknown/foreign PLMN.
 * Related: https://github.com/open5gs/open5gs/issues/3544 */
testcase TC_ng_setup_unknown_global_gnb_id_plmn() runs on MTC_CT {
	f_init();
	g_ngran_pars[0].global_ngran_id.globalGNB_ID.pLMNIdentity := '62F224'O;
	f_ngap_setup(0);
}

/* Unsuccessful NG Setup procedure to AMF (wrong PLMN) */
testcase TC_ng_setup_wrong_tac() runs on MTC_CT {
	f_init();
	g_ngran_pars[0].supported_ta_list[0].broadcastPLMNList[0].pLMNIdentity := '62F224'O;
	f_ngap_setup(0, {misc:=unknown_PLMN_or_SNPN});
}

/* NG Setup procedure to 5GC using a correct Global gNB ID. */
testcase TC_ng_setup() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);
}

private function f_TC_register() runs on ConnHdlr {
	f_register();
	f_deregister();
}
testcase TC_ng_register() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_register), pars);
	vc_conn.done;
}

private function f_TC_periodic_registration_updating() runs on ConnHdlr {
	f_register();
	f_periodic_register_update();
	f_deregister();
}
testcase TC_periodic_registration_updating() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_periodic_registration_updating), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.2.6 AN Release */
private function f_TC_ue_context_release_no_pdu_session() runs on ConnHdlr {
	f_register();
	f_ue_context_release();
}
testcase TC_ue_context_release_no_pdu_session() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_context_release_no_pdu_session), pars);
	vc_conn.done;
}
private function f_TC_ue_context_release_with_pdu_session() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(configure_userplane := false);
	f_sleep(1.0);
	f_ue_context_release();
}
testcase TC_ue_context_release_with_pdu_session() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_context_release_with_pdu_session), pars);
	vc_conn.done;
}

private function f_TC_pdu_sess_modification() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(configure_userplane := false);
	f_sleep(1.0);
	f_pdu_sess_modify();
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_pdu_sess_modification() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_pdu_sess_modification), pars);
	vc_conn.done;
}

/* 3GPP TS 24.501 8.3.1.7 Always-on PDU session requested (see also 9.11.4.4)
 * 3GPP TS 24.501 8.3.2.6 Always-on PDU session indication (see also 9.11.4.3) */
private function f_TC_pdu_sess_est_pco_pcscf() runs on ConnHdlr {
	var template (value) ExtdProtocolConfigOptions pco_req;
	var template (present) ExtdProtocolConfigOptions pco_exp;
	var NG_PDU_SESSION_ESTABLISHMENT_ACCEPT sess_acc;

	f_register();
	pco_req := cs_ExtdPCO('0000'O,
			     { cs_ProtocolContainer('000C'O, ''O),
			       cs_ProtocolContainer('0001'O, ''O) });
	pco_exp := cr_ExtdPCO(?,
			      { cs_ProtocolContainer('000C'O, f_inet_addr(C5G_Tests.mp_pcscf_hostname4)),
			        cs_ProtocolContainer('0001'O, f_inet6_addr(C5G_Tests.mp_pcscf_hostname6)) });

	sess_acc := f_pdu_sess_establish(p_ExtdPCO := pco_req, configure_userplane := false);

	if (not match(sess_acc.extdProtocolConfigurationOptions, pco_exp)) {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
			log2str("Requested ExtPCO ", pco_req, " expected ", pco_exp,
				" got ", sess_acc.extdProtocolConfigurationOptions));
	}

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_pdu_sess_est_pco_pcscf() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_pdu_sess_est_pco_pcscf), pars);
	vc_conn.done;
}

/* 3GPP TS 24.501 8.3.1.7 Always-on PDU session requested (see also 9.11.4.4)
 * 3GPP TS 24.501 8.3.2.6 Always-on PDU session indication (see also 9.11.4.3) */
private function f_TC_pdu_sess_est_always_on() runs on ConnHdlr {
	var NG_PDU_SESSION_ESTABLISHMENT_ACCEPT sess_acc;

	f_register();
	sess_acc := f_pdu_sess_establish(p_AlwaysOnPDUSessionReq := crs_AlwaysOn('1'B),
					 configure_userplane := false);

	if (not ispresent(sess_acc.alwaysOnPDUSessionInd)) {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
					"Requested Always-on PDU but got no Always-on Indication");
	} else if(match(sess_acc.alwaysOnPDUSessionInd, cs_AlwaysOnNotAllowed)) {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
					"Requested Always-on PDU but got Always-on Not Allowed");
	}

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_pdu_sess_est_always_on() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_pdu_sess_est_always_on), pars);
	vc_conn.done;
}

private function f_TC_register_ping4() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(pdu_sess_type := c_PDU_SessionType_IPv4);
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_ng_register_ping4() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_register_ping4), pars);
	vc_conn.done;
}
testcase TC_ng_register_ping4_256() runs on MTC_CT {
	var ConnHdlr vc_conn[256];
	var integer i;

	f_init();
	f_ngap_setup(0);

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		var ConnHdlrPars pars := f_init_pars(ue_idx := i);
		vc_conn[i] := f_start_handler_with_pars(refers(f_TC_register_ping4), pars);
	}

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		vc_conn[i].done;
	}
}

private function f_TC_register_ping6() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(pdu_sess_type := c_PDU_SessionType_IPv6);
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_ng_register_ping6() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	pars.ue_pars.sess_pars.run_prog_pars.ping_hostname := mp_ping_hostname6;
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_register_ping6), pars);
	vc_conn.done;
}
testcase TC_ng_register_ping6_256() runs on MTC_CT {
	var ConnHdlr vc_conn[256];
	var integer i;

	f_init();
	f_ngap_setup(0);

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		var ConnHdlrPars pars := f_init_pars(ue_idx := i);
		pars.ue_pars.sess_pars.run_prog_pars.ping_hostname := mp_ping_hostname6;
		vc_conn[i] := f_start_handler_with_pars(refers(f_TC_register_ping6), pars);
	}

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		vc_conn[i].done;
	}
}

private function f_TC_register_ping46() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(pdu_sess_type := c_PDU_SessionType_IPv4v6);
	f_sleep(1.0);
	f_ping(mp_ping_hostname);
	/* Give some extra time for previous commands on all ConnHdlr finish,
	 * so we don't run out of PIDs in the PID table of GTPv1U_Emulation: */
	f_sleep(5.0);
	f_ping(mp_ping_hostname6);
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_ng_register_ping46() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_register_ping46), pars);
	vc_conn.done;
}
testcase TC_ng_register_ping46_256() runs on MTC_CT {
	var ConnHdlr vc_conn[256];
	var integer i;

	f_init();
	f_ngap_setup(0);

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		var ConnHdlrPars pars := f_init_pars(ue_idx := i);
		vc_conn[i] := f_start_handler_with_pars(refers(f_TC_register_ping46), pars);
	}

	for (i := 0; i < sizeof(vc_conn); i := i + 1) {
		vc_conn[i].done;
	}
}

/* 3GPP TS 23.502 4.2.3.2 UE Triggered Service Request.
 * UE signals the active session as active, and it has UL data for it */
private function f_TC_ue_service_request_cm_idle_ul_data() runs on ConnHdlr {
	var PDU_SessionStatus pdu_session_status;

	f_register();
	f_pdu_sess_establish(configure_userplane := false);
	f_sleep(1.0);
	f_ue_context_release();
	f_sleep(1.0);
	pdu_session_status := f_PDU_SessionStatus();
	f_service_request_cm_idle(ul_data_status := pdu_session_status,
				  pdu_sess_status := pdu_session_status);

	as_ngap_handle_InitialCtxReq_withPDUSessionList();

	NGAP.receive(cr_NG_SERVICE_ACCEPT(p_PDU_SessionStatus := cr_PDU_SessionStatus('00000010'B, '00000000'B)));

	f_deregister();
}
testcase TC_ue_service_request_cm_idle_ul_data() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_service_request_cm_idle_ul_data), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.2.3.2 UE Triggered Service Request.
 * UE signals that the active session is now inactive. AMF sends a PFCP Session Delete to SMF. */
private function f_TC_ue_service_request_cm_idle_inact_sess() runs on ConnHdlr {
	var NGAP_PDU rx_ngap;

	f_register();
	f_pdu_sess_establish(configure_userplane := false);
	f_sleep(1.0);
	f_ue_context_release();
	f_sleep(1.0);
	f_service_request_cm_idle(pdu_sess_status := cs_PDU_SessionStatus('00000000'B, '00000000'B));
	NGAP.receive(cr_NG_SERVICE_ACCEPT(p_PDU_SessionStatus := cr_PDU_SessionStatus('00000000'B, '00000000'B)));
	g_pars.ue_pars.amf_id := f_ngap_obtain_amf_id();
	f_deregister();
}
testcase TC_ue_service_request_cm_idle_inact_sess() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_service_request_cm_idle_inact_sess), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.2.3.2 UE Triggered Service Request.
 * UE signals that a session unknown to network is active: */
private function f_TC_ue_service_request_cm_idle_unknown_sess_active() runs on ConnHdlr {
	f_register();
	f_sleep(1.0);
	f_ue_context_release();
	f_sleep(1.0);
	f_service_request_cm_idle(pdu_sess_status := cs_PDU_SessionStatus('00000010'B, '00000000'B));
	NGAP.receive(cr_NG_SERVICE_ACCEPT(p_PDU_SessionStatus := cr_PDU_SessionStatus('00000000'B, '00000000'B)));
	g_pars.ue_pars.amf_id := f_ngap_obtain_amf_id();
	f_deregister();
}
testcase TC_ue_service_request_cm_idle_unknown_sess_active() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_service_request_cm_idle_unknown_sess_active), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.2.3.2 UE Triggered Service Request. While in CM-CONNECTED state. */
private function f_TC_ue_service_request_cm_connected() runs on ConnHdlr {
	var PDU_SessionStatus pdu_session_status;

	f_register();
	f_pdu_sess_establish(configure_userplane := false);
	pdu_session_status := f_PDU_SessionStatus();
	f_service_request_cm_connected(ul_data_status := cs_PDU_SessionStatus('00000000'B, '00000000'B),
				       pdu_sess_status := pdu_session_status);
	NGAP.receive(cr_NG_SERVICE_ACCEPT(p_PDU_SessionStatus := cr_PDU_SessionStatus('00000010'B, '00000000'B)));
	f_deregister();
}
testcase TC_ue_service_request_cm_connected() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ue_service_request_cm_connected), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.2.3.3 Network Triggered Service Request */
private function f_TC_net_triggered_service_req() runs on ConnHdlr {
	var PDU_SessionStatus pdu_session_status;

	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);
	f_ue_context_release();
	f_sleep(1.0);
	f_create_ngap_expect_proc(id_Paging, self);

	/* NOTE: In theory we should generate DL-originated traffic here, but since
	 * UPF let our UL traffic pass (bug?), make use of it to generate some DL
	 * traffic (ping reply) triggering the Paging: */
	var integer pid := f_ping_async(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname, interval := 1, count := 1);

	NGAP.receive(mw_ngap_initMsg(mw_n2_Paging(?, ?)));
	log("Rx Paging")

	/* We emulate UE in CM-IDLE: */
	pdu_session_status := f_PDU_SessionStatus();
	f_service_request_cm_idle(pdu_sess_status := pdu_session_status);
	/* open5gs GH#771: open5gs-amfd sends InitialContextSetupRequest instead of PDUSessionResourceSetupRequest */
	as_ngap_handle_InitialCtxReq_withPDUSessionList();
	NGAP.receive(cr_NG_SERVICE_ACCEPT(p_PDU_SessionStatus := cr_PDU_SessionStatus('00000010'B, '00000000'B)));
	as_ngap_handle_configuration_update();

	f_gtp1u_wait_term(pid, 0, 1.0);

	f_deregister();
}
testcase TC_net_triggered_service_req() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_net_triggered_service_req), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.8.1.1(a) Connection Inactive procedure */
private function f_TC_connection_inactive() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(configure_userplane := false);

	/* Figure 4.8.1.1a-1 step 2: "NG-RAN sends N2 MT Communication Handling request message
	 * to AMF indicating the UE is transitioning to RRC_INACTIVE state". */
	f_mt_communication_handling();


	f_sleep(1.0);
	f_ue_context_release();
}
testcase TC_connection_inactive() runs on MTC_CT {
	var ConnHdlr vc_conn;
	var ConnHdlrPars pars;

	f_init();
	f_ngap_setup(0);

	pars := f_init_pars(ue_idx := 0);
	/* Validate we received from CN in "INITIAL CONTEXT SETUP REQUEST":
	 * "CN MT Communication Handling IE" inside "Core Network Assistance Information for RRC INACTIVE IE",
	 * 3GPP TS 38.413 9.3.1.15.
	 * NOTE: current open5gs v2.7.3 doesn't set it, meaning it doesn't support RRC_INACTIVE state. */
	pars.exp_CoreNetworkAssistanceInformationForInactive := true;

	vc_conn := f_start_handler_with_pars(refers(f_TC_connection_inactive), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.8.1.2 Connection Suspend procedure,
 * 3GPP TS 23.502 4.8.2.3 Connection Resume procedure */
private function f_TC_connection_suspend() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish(configure_userplane := false);

	f_ue_context_suspend();
	f_ue_context_resume();
	f_sleep(1.0);
	f_ue_context_release();
}
testcase TC_connection_suspend() runs on MTC_CT {
	var ConnHdlr vc_conn;
	var ConnHdlrPars pars;

	/* TODO: 3GPP TS 23.501 5.31.18 "User Plane CIoT 5GS Optimisation":
	 * - UE and AMF negotiated support User Plane CIoT 5GS Optimisation (see clause 5.31.2) over NAS,
	 * - the UE has indicated support of User Plane CIoT 5GS Optimisation in the UE radio capabilities as defined in TS 36.331 [51]
	 * AMF has indicated User Plane CIoT 5GS Optimisation support for the UE to NG-RAN
	 */
	f_init();
	f_ngap_setup(0);

	pars := f_init_pars(ue_idx := 0);

	vc_conn := f_start_handler_with_pars(refers(f_TC_connection_suspend), pars);
	vc_conn.done;
}

/* 3GPP TS 23.501 5.16.4 Emergency Services
 * 3GPP TS 23.502 4.13.4 Emergency Services
 */
private function f_TC_normal_reg_emergency_services() runs on ConnHdlr {
	/* Validate this is supported in Register Accept:
	 * .... 00.. = Emergency service support indicator (EMC): Emergency services not supported (0)
	 * This will fail currently, see https://github.com/open5gs/open5gs/issues/4359
	 */
	var template (present) NG_NAS_DL_Message_Type exp_registration_accept :=
		cr_NG_REGISTRATION_ACCEPT(p_NwkFeatureSupport := cr_NG_NetworkFeatureSupport(emc := '11'B),
					  p_EmergNumList := *);
	f_register(exp_registration_accept := exp_registration_accept);
	f_pdu_sess_establish(request_type := c_RequestType_initial_emergency_request);
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_normal_reg_emergency_services() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_normal_reg_emergency_services), pars);
	vc_conn.done;
}

/* 3GPP TS 23.501 5.16.4.11 Emergency Services Fallback
 * 3GPP TS 23.502 4.13.4.2 Emergency Services Fallback
 */
private function f_TC_normal_reg_emergency_services_fallback() runs on ConnHdlr {
	/* Validate this is supported in Register Accept:
	 * ..00 .... = Emergency service fallback indicator (EMF): Emergency services fallback not supported (0)
	 * This will fail currently, see https://github.com/open5gs/open5gs/issues/4360
	 */
	var template (present) NG_NAS_DL_Message_Type exp_registration_accept :=
		cr_NG_REGISTRATION_ACCEPT(p_NwkFeatureSupport := cr_NG_NetworkFeatureSupport(emf := '11'B));
	f_register(exp_registration_accept := exp_registration_accept);

	f_service_request_emergency_services_fallback();

	/* TODO: "After receiving the Service Request for Emergency Fallback,
	 * the AMF triggers N2 procedure resulting in either CONNECTED state mobility
	 * (Handover procedure) or IDLE state mobility (redirection) to either E-UTRA/5GC
	 * or to E-UTRAN/EPC depending on factors such as N26 availability, network
	 * configuration and radio conditions."
	 * Since the following cannot yet be configured in open5gs, we cannot really
	 * implement it either here. See also 3GPP TS 23.502 4.13.4.2 step 4 + 5.
	 * Currently open5gs answers the Service Request with a Service Accept, which is wrong.
	 */

	f_sleep(1.0);

	f_deregister();
}
testcase TC_normal_reg_emergency_services_fallback() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_normal_reg_emergency_services_fallback), pars);
	vc_conn.done;
}

/* 3GPP TS 23.501 5.16.4 Emergency Services
 * 3GPP TS 23.502 4.13.4 Emergency Services
 */
private function f_TC_emergency_registration() runs on ConnHdlr {
	/* Validate this is supported in Register Accept:
	 * .... 00.. = Emergency service support indicator (EMC): Emergency services not supported (0)
	 * This will fail currently, see https://github.com/open5gs/open5gs/issues/4359
	 */
	var template (present) NG_NAS_DL_Message_Type exp_registration_accept :=
		cr_NG_REGISTRATION_ACCEPT(p_NwkFeatureSupport := cr_NG_NetworkFeatureSupport(emc := '11'B),
					  p_EmergNumList := *);
	f_register(registration_type := tsc_NG_RegistrationEmergency,
		   exp_registration_accept := exp_registration_accept);
	f_pdu_sess_establish(request_type := c_RequestType_initial_emergency_request);
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);
	f_pdu_sess_release();
	f_deregister();
}
testcase TC_emergency_registration() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_emergency_registration), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.9.1.2.2 Xn based inter NG-RAN handover without User Plane function re-allocation */
private function f_TC_handover_inter_ngran_xn_source() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	var NG_NAS_UE_State nus := f_ngap_obtain_nus(g_pars.ue_pars.ran_id);

	/* TODO: conditional, transmit "1a. RAN Usage data report" */
	COORD.send(Handover_Xn_Pars:{source_amf_id := g_pars.ue_pars.amf_id,
				     guti := g_pars.ue_pars.guti,
				     pti := g_pars.ue_pars.pti,
				     sess_pars := g_pars.ue_pars.sess_pars,
				     kset_id := g_pars.kset_id,
				     nus := nus });
}
private function f_TC_handover_inter_ngran_xn_target() runs on ConnHdlr {
	var Handover_Xn_Pars ho_xn_pars;

	COORD.receive(Handover_Xn_Pars:?) -> value ho_xn_pars;
	log("HANDOVER STARTED!");
	g_pars.ue_pars.guti := ho_xn_pars.guti;
	g_pars.ue_pars.pti := ho_xn_pars.pti;
	g_pars.ue_pars.sess_pars.id := ho_xn_pars.sess_pars.id;
	g_pars.ue_pars.sess_pars.cn_gtpu_ip := ho_xn_pars.sess_pars.cn_gtpu_ip;
	g_pars.ue_pars.sess_pars.cn_gtpu_teid := ho_xn_pars.sess_pars.cn_gtpu_teid;
	g_pars.ue_pars.sess_pars.qos_rules := ho_xn_pars.sess_pars.qos_rules;
	g_pars.ue_pars.sess_pars.ue_ip4 := ho_xn_pars.sess_pars.ue_ip4;
	g_pars.ue_pars.sess_pars.ue_ip6 := ho_xn_pars.sess_pars.ue_ip6;
	g_pars.kset_id := ho_xn_pars.kset_id;

	f_n2_path_switch(ho_xn_pars.source_amf_id);
	f_ngap_set_nus(g_pars.ue_pars.ran_id, ho_xn_pars.nus)
	f_sleep(1.0);

	f_pdu_sess_create_tun();
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_handover_inter_ngran_xn() runs on MTC_CT {
	var ConnHdlr vc_conn[2];
	var integer i;

	f_init();
	f_ngap_setup(0);
	f_ngap_setup(1);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0, c5g_idx := 0);
	vc_conn[0] := f_start_handler_create(ngap_idx := 0);
	vc_conn[1] := f_start_handler_create(ngap_idx := 1);
	connect(vc_conn[0]:COORD, vc_conn[1]:COORD);

	f_start_handler_run(vc_conn[0], refers(f_TC_handover_inter_ngran_xn_source), pars);
	pars.c5g_idx := 1;
	pars.ue_pars.sess_pars.ran_gtpu_teid := int2oct(oct2int(pars.ue_pars.sess_pars.ran_gtpu_teid) + 1, 4);
	f_start_handler_run(vc_conn[1], refers(f_TC_handover_inter_ngran_xn_target), pars);

	vc_conn[0].done;
	vc_conn[1].done;
}

/* 3GPP TS 23.502 4.9.1.3 Inter NG-RAN node N2 based handover */
private function f_TC_handover_inter_ngran_n2_source() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	var NG_NAS_UE_State nus := f_ngap_obtain_nus(g_pars.ue_pars.ran_id);

	COORD.send(Handover_n2_Pars:{
				guti := g_pars.ue_pars.guti,
				pti := g_pars.ue_pars.pti,
				sess_pars := g_pars.ue_pars.sess_pars,
				kset_id := g_pars.kset_id,
				nus := nus
			});

	/* 3GPP TS 23.502 4.9.1.3.2 step 1: */
	f_n2_handover_required(m_targetID_targetRANNodeID(m_targetRANNodeID(g_pars.ngran_pars[1].global_ngran_id,
									    ts_ngran_NGAP_TAI(g_pars.ngran_pars[1]))
							));
	/* 3GPP TS 23.502 4.9.1.3.3 step 1: */
	f_n2_handover_command();
	/* 3GPP TS 23.502 4.9.1.3.3 step 2: */
	COORD.send("HANDOVER_COMMAND_CONFIRM");

	/* 3GPP TS 23.502 4.9.1.3.3 step 14a + 14b: */
	as_ngap_handle_UeContextReleaseCmd(exp_cause := mw_cause_radioNetwork(successful_handover));
}
private function f_TC_handover_inter_ngran_n2_target() runs on ConnHdlr {
	var Handover_n2_Pars ho_n2_pars;

	/* Redirect Handover Request from AMF containing new AMF ID to our target ConnHdlr: */
	f_create_ngap_expect_proc(id_HandoverResourceAllocation, self);

	COORD.receive(Handover_n2_Pars:?) -> value ho_n2_pars;
	g_pars.ue_pars.guti := ho_n2_pars.guti;
	g_pars.ue_pars.pti := ho_n2_pars.pti;
	g_pars.ue_pars.sess_pars.id := ho_n2_pars.sess_pars.id;
	g_pars.ue_pars.sess_pars.qos_rules := ho_n2_pars.sess_pars.qos_rules;
	g_pars.ue_pars.sess_pars.ue_ip4 := ho_n2_pars.sess_pars.ue_ip4;
	g_pars.ue_pars.sess_pars.ue_ip6 := ho_n2_pars.sess_pars.ue_ip6;
	g_pars.kset_id := ho_n2_pars.kset_id;


	/* 3GPP TS 23.502 4.9.1.3.2 step 9+10: */
	f_n2_handover_request();

	/* 3GPP TS 23.502 4.9.1.3.3 step 4 */
	COORD.receive("HANDOVER_COMMAND_CONFIRM");
	f_ngap_set_nus(g_pars.ue_pars.ran_id, ho_n2_pars.nus);

	/* 3GPP TS 23.502 4.9.1.3.3 step 5: */
	f_n2_handover_notify();

	f_sleep(1.0);

	f_pdu_sess_create_tun();
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_handover_inter_ngran_n2() runs on MTC_CT {
	var ConnHdlr vc_conn[2];
	var integer i;

	f_init();
	f_ngap_setup(0);
	f_ngap_setup(1);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0, c5g_idx := 0);
	vc_conn[0] := f_start_handler_create(ngap_idx := 0);
	vc_conn[1] := f_start_handler_create(ngap_idx := 1);
	connect(vc_conn[0]:COORD, vc_conn[1]:COORD);

	f_start_handler_run(vc_conn[0], refers(f_TC_handover_inter_ngran_n2_source), pars);
	pars.c5g_idx := 1;
	pars.ue_pars.sess_pars.ran_gtpu_teid := int2oct(oct2int(pars.ue_pars.sess_pars.ran_gtpu_teid) + 1, 4);
	f_start_handler_run(vc_conn[1], refers(f_TC_handover_inter_ngran_n2_target), pars);

	vc_conn[0].done;
	vc_conn[1].done;
}

/* 3GPP TS 23.502 4.9.1.4 Inter NG-RAN node N2 based handover, Cancel */
private function f_TC_handover_inter_ngran_n2_cancel_source() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	/* 3GPP TS 23.502 4.9.1.3.2 step 1: */
	f_n2_handover_required(m_targetID_targetRANNodeID(m_targetRANNodeID(g_pars.ngran_pars[1].global_ngran_id,
									    ts_ngran_NGAP_TAI(g_pars.ngran_pars[1]))
							));
	/* 3GPP TS 23.502 4.9.1.3.3 step 1: */
	f_n2_handover_command();
	/* 3GPP TS 23.502 4.9.1.3.3 step 2: */
	f_n2_handover_cancel();

	/* Test UE can still use the network after HO cancel: */
	f_ping(g_pars.ue_pars.sess_pars.run_prog_pars.ping_hostname);

	f_pdu_sess_release();
	f_deregister();
}
private function f_TC_handover_inter_ngran_n2_cancel_target() runs on ConnHdlr {

	/* Redirect Handover Request from AMF containing new AMF ID to our target ConnHdlr: */
	f_create_ngap_expect_proc(id_HandoverResourceAllocation, self);

	/* 3GPP TS 23.502 4.9.1.3.2 step 9+10: */
	f_n2_handover_request();

	/* Expect to get a release on the target gNB due to cancelled Handover: */
	as_ngap_handle_UeContextReleaseCmd(exp_cause := mw_cause_radioNetwork(handover_cancelled));
}
testcase TC_handover_inter_ngran_n2_cancel() runs on MTC_CT {
	var ConnHdlr vc_conn[2];
	var integer i;

	f_init();
	f_ngap_setup(0);
	f_ngap_setup(1);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0, c5g_idx := 0);
	vc_conn[0] := f_start_handler_create(ngap_idx := 0);
	vc_conn[1] := f_start_handler_create(ngap_idx := 1);
	connect(vc_conn[0]:COORD, vc_conn[1]:COORD);

	f_start_handler_run(vc_conn[0], refers(f_TC_handover_inter_ngran_n2_cancel_source), pars);
	pars.c5g_idx := 1;
	pars.ue_pars.sess_pars.ran_gtpu_teid := int2oct(oct2int(pars.ue_pars.sess_pars.ran_gtpu_teid) + 1, 4);
	f_start_handler_run(vc_conn[1], refers(f_TC_handover_inter_ngran_n2_cancel_target), pars);

	vc_conn[0].done;
	vc_conn[1].done;
}

/* 3GPP TS 23.502 4.21 Secondary RAT Usage Data Reporting Procedure
 * 3GPP TS 38.413 8.15.1 Secondary RAT Data Usage Report
 * 3GPP TS 29.502 5.2.2.3.7 RAN Initiated QoS Flow Mobility
 */
private function f_TC_ran_initiated_qos_flow_mobility() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);

	f_pdu_sess_modify_ind();

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_ran_initiated_qos_flow_mobility() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_ran_initiated_qos_flow_mobility), pars);
	vc_conn.done;
}

/* 3GPP TS 23.502 4.21 Secondary RAT Usage Data Reporting Procedure
 * 3GPP TS 38.413 8.15.1 Secondary RAT Data Usage Report
 */
private function f_TC_secondary_rat_data_usage_report() runs on ConnHdlr {
	f_register();
	f_pdu_sess_establish();
	f_sleep(1.0);

	f_secondary_rat_data_usage_report();

	f_pdu_sess_release();
	f_deregister();
}
testcase TC_secondary_rat_data_usage_report() runs on MTC_CT {
	f_init();
	f_ngap_setup(0);

	var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
	var ConnHdlr vc_conn;
	vc_conn := f_start_handler_with_pars(refers(f_TC_secondary_rat_data_usage_report), pars);
	vc_conn.done;
}

control {
	execute( TC_ng_setup() );
	execute( TC_ng_setup_unknown_global_gnb_id_plmn() );
	execute( TC_ng_setup_wrong_tac() );
	execute( TC_ng_register() );
	execute( TC_periodic_registration_updating() );
	execute( TC_ue_context_release_no_pdu_session() );
	execute( TC_ue_context_release_with_pdu_session() );
	execute( TC_pdu_sess_modification() );
	execute( TC_pdu_sess_est_always_on() );
	execute( TC_pdu_sess_est_pco_pcscf() );

	execute( TC_ng_register_ping4() );
	execute( TC_ng_register_ping4_256() );

	execute( TC_ng_register_ping6() );
	execute( TC_ng_register_ping6_256() );

	execute( TC_ng_register_ping46() );
	execute( TC_ng_register_ping46_256() );

	execute( TC_ue_service_request_cm_idle_ul_data() );
	execute( TC_ue_service_request_cm_idle_inact_sess() );
	execute( TC_ue_service_request_cm_idle_unknown_sess_active() );
	execute( TC_ue_service_request_cm_connected() );

	execute( TC_net_triggered_service_req() );
	execute( TC_connection_inactive() );
	execute( TC_connection_suspend() );

	execute( TC_normal_reg_emergency_services() );
	execute( TC_normal_reg_emergency_services_fallback() );
	execute( TC_emergency_registration() );

	/* OS#7023: Don't run against latest until a > v2.8.0 release was made
	 * with d2707b0c39c2bf055646184e108ca006c728e443 */
	if (Misc_Helpers.f_osmo_repo_is("nightly")) {
		execute( TC_handover_inter_ngran_xn() );
	}

	execute( TC_handover_inter_ngran_n2() );
	execute( TC_handover_inter_ngran_n2_cancel() );

	execute( TC_ran_initiated_qos_flow_mobility() );
	execute( TC_secondary_rat_data_usage_report() );
}

/* TODO:
 * - 3GPP TS 23.502 4.2.2.2.3 Registration with AMF re-allocation: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4214).
 * - 3GPP TS 23.502 4.2.2.2.4 Registration with Onboarding SNPN: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4215)
 * - 3GPP TS 23.502 4.2.2.3.3 Network-initiated Deregistration: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4216)
 * - 3GPP TS 23.502 4.2.4.3 UE Configuration Update procedure for transparent UE Policy delivery
 * - 3GPP TS 23.502 4.2.5.2 UE Reachability Notification Request procedure: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4318)
 * - 3GPP TS 23.502 4.2.5.3 UE Activity Notification procedure: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4318)
 * - 3GPP TS 23.502 4.2.9.2 Network Slice-Specific Authentication and Authorization: open5gs-nsssafd not implemented, nssaa interface not implemented in open5gs-amfd.
 * - 3GPP TS 23.502 4.2.9.3 AAA Server triggered Network Slice-Specific Re-authentication and Re-authorization procedure: open5gs-nsssafd not implemented, Nnssaaf_NSSAA_Re-AuthNotification not implemented in open5gs-amfd (Nudm_UECM_Get is implemented in open5gs-udm).
 * - 3GPP TS 23.502 4.2.9.4 AAA Server triggered Slice-Specific Authorization Revocation: open5gs-nsssafd not implemented, Nnssaaf_NSSAA_RevocationNotification not implemented in open5gs-amfd (Nudm_UECM_Get is implemented in open5gs-udm).
 * - 3GPP TS 23.502 4.5.2 Session Management Subscriber Data Update Notification to SMF: open5gs-udm supports sotring subscriptions, but never triggers notifications whenever subscriber data is changed. open5gs-smfd registers to UDM but doesn't implement receiving a notification.
 * - 3GPP TS 23.502 4.7 ME Identity check procedure: N5g-eir (OGS_SBI_SERVICE_NAME_N5G_EIR_EIC) not implemented
 * - 3GPP TS 23.502 4.8.1.1(a) Connection Inactive procedure: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4319)
 * - 3GPP TS 23.502 4.8.1.2 Connection Suspend procedure: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4322)
 * - 3GPP TS 23.502 4.8.2 Connection Resume procedure: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4322)
 * - 3GPP TS 23.502 4.11.1.2.1 5GS to EPS handover using N26 interface: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.11.1.2.2 EPS to 5GS handover using N26 interface: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.11.1.2.3 Handover Cancel: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.11.1.3.2 5GS to EPS Idle mode mobility using N26 interface: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.11.1.3.3 EPS to 5GS Mobility Registration Procedure (Idle and Connected State) using N26 interface: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.11.2 Interworking procedures without N26 interface: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.12 Procedures for Untrusted non-3GPP access: Seems transparent to the 5GC?
 * - 3GPP TS 23.502 4.12a Procedures for Trusted non-3GPP access: Seems transparent to the 5GC?
 * - 3GPP TS 23.502 4.13.3 SMS over NAS procedures: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.3.3 MO SMS over NAS in CM-IDLE (baseline): Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.3.5 MO SMS over NAS in CM-CONNECTED: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.3.6 MT SMS over NAS in CM-IDLE state and RRC_INACTIVE with CN based MT communication state via 3GPP access: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.3.7 MT SMS over NAS in CM-CONNECTED state via 3GPP access: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.3.8 MT SMS over NAS via non-3GPP access: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4362)
 * - 3GPP TS 23.502 4.13.5 Location Services procedures: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/4364)
 * - 3GPP TS 23.502 4.13.6.1 EPS fallback for IMS voice: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.13.6.2 Inter RAT Fallback in 5GC for IMS voice: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.502 4.13.6.3 Transfer of PDU session used for IMS voice from non-3GPP access to 5GS: Not supported by open5gs (https://github.com/open5gs/open5gs/issues/2534)
 * - 3GPP TS 23.632 5.3.2 Mobility from 5GC to EPC: Nudm_UECM_AMFDeregistration not supported by open5gs
 * - 3GPP TS 23.632 5.3.3 Mobility from EPC to 5GC: Not supported by open5gs (missing Nhss interface use in UDM)
 * - 3GPP TS 23.632 5.2.3 Vector Generation in UDM/ARPF: Not supported by open5gs (missing Nudm_UEAuthentication_GetHssAv in UDM)
 * - 3GPP TS 23.502 Annex G (normative): Support of GERAN/UTRAN access by SMF+PGW-C: Not supported by open5gs (Gn<->N7 (to PCF) in open5gs-smfd)
 */

}
