module SGSN_Tests_Iu {

import from Osmocom_Types all;
import from Osmocom_VTY_Functions all;
import from Misc_Helpers all;

import from SGSN_Tests all;
import from BSSGP_Emulation all;

import from RAN_Adapter all;
import from RAN_Emulation all;
import from RANAP_Templates all;
import from RANAP_PDU_Descriptions all;
import from RANAP_IEs all;

import from GTPv1C_Templates all;
import from GTPv1U_Templates all;
import from GTP_Emulation all;

import from BSSGP_ConnHdlr all;


private function f_init() runs on test_CT {
	g_iu_enable := true;
	SGSN_Tests.f_init();

	/* default VTY configuration */
	f_vty_config(SGSNVTY, "sgsn", "encryption uea 0");
}

private function f_vty_iu_rab_assign_addr_enc(boolean use_itu_x213 := true) runs on test_CT {
	if (use_itu_x213) {
		f_vty_config(SGSNVTY, "sgsn", "iu rab-assign-addr-enc x213");
	} else {
		f_vty_config(SGSNVTY, "sgsn", "iu rab-assign-addr-enc v4raw");
	}
}

private function f_TC_iu_attach(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);
	setverdict(pass);
}
testcase TC_iu_attach() runs on test_CT {
	/* MS -> SGSN: Attach Request IMSI
	 * MS <- SGSN: Identity Request IMEI
	 * MS -> SGSN: Identity Response IMEI
	 * MS <- SGSN: Auth Request
	 * MS -> SGSN: Auth Response
	 * MS <- SGSN: Attach Accept
	 * MS -> SGSN: Attach Complete
	 */
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	f_vty_config(SGSNVTY, "sgsn", "encryption uea 0");
	vc_conn := f_start_handler(refers(f_TC_iu_attach), testcasename(), g_gb, 1001);
	vc_conn.done;
	f_cleanup();
}

testcase TC_iu_attach_encr() runs on test_CT {
	/* MS -> SGSN: Attach Request IMSI
	 * MS <- SGSN: Identity Request IMEI
	 * MS -> SGSN: Identity Response IMEI
	 * MS <- SGSN: Auth Request
	 * MS -> SGSN: Auth Response
	 * MS <- SGSN: Security Mode Command
	 * MS -> SGSN: Security Mode Complete
	 * hNodeB <- SGSN: Common Id
	 * MS <- SGSN: Attach Accept
	 * MS -> SGSN: Attach Complete
	 */
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_vty_config(SGSNVTY, "sgsn", "encryption uea 1 2");
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_iu_attach), testcasename(), g_gb, 1001, expect_ciph := true);
	vc_conn.done;
	f_cleanup();
}

private function f_TC_iu_attach_geran_rau(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);

	/* we got a new TLLI, register it */
	f_bssgp_client_unregister(g_pars.imsi);
	f_bssgp_client_register(g_pars.imsi, g_pars.tlli);

	/* do a routing area update */
	f_routing_area_update(g_pars.ra);
}

testcase TC_iu_attach_geran_rau() runs on test_CT {
	/* MS <-> SGSN: Successful Attach over Iu
	 * MS <-> SGSN: Routing Area Update over Geran
	 */
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_iu_attach_geran_rau), testcasename(), g_gb, 1002);
	vc_conn.done;
	f_cleanup();
}

private function f_TC_geran_attach_iu_rau(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 0);

	/* do a routing area update */
	f_routing_area_update(g_pars.ra, ran_index := 3);
	setverdict(pass);
}

testcase TC_geran_attach_iu_rau() runs on test_CT {
	/* MS <-> SGSN: Successful Attach over Geran
	 * MS <-> SGSN: Routing Area Update over Iu
	 */
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_geran_attach_iu_rau), testcasename(), g_gb, 1003);
	vc_conn.done;
	f_cleanup();
}

private function f_TC_attach_pdp_act_user(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);

	f_service_request(apars, ran_index := 3);

	f_pdp_ctx_act(apars, ran_index := 3);

	/* then transceive a downlink PDU */
	f_gtpu_xceive_mt(apars, f_rnd_octstring(100), ran_index := 3);
	f_gtpu_xceive_mo(apars, f_rnd_octstring(200), ran_index := 3);

	f_pdp_ctx_deact_mo(apars, '00'O, ran_index := 3);
	setverdict(pass);
}
testcase TC_attach_pdp_act_user() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_user), testcasename(), g_gb, 1004);
	vc_conn.done;
	f_cleanup();
}

/* Same as TC_attach_pdp_act_user, but:
 * - Configure SGSN to send v4raw address in RAB Ass REQ
 * - Transmit HNBGW addr using v4raw format in RAB Ass Resp.
 */
testcase TC_attach_pdp_act_user_addr_v4raw() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	var BSSGP_ConnHdlrPars pars;
	f_init();
	f_sleep(1.0);
	f_vty_iu_rab_assign_addr_enc(false); /* Force v4raw */
	pars := f_new_BSSGP_ConnHdlrPars(1058, f_cell_ids_from_gb(g_gb));
	pars.ranap_exp_itu_x213_addr_format := false;
	pars.ranap_use_itu_x213_addr_format := false;
	vc_conn := f_start_handler_pars(refers(f_TC_attach_pdp_act_user), testcasename(), g_gb, pars);
	vc_conn.done;
	f_cleanup();
}
/* Same as TC_attach_pdp_act_user, but:
 * - Configure SGSN to send ITU X.213 address in RAB Ass REQ
 * - Transmit HNBGW addr using ITU X.213 format (IPv4, with different padding)
 *   in RAB Ass Resp. See OS#6508.
 */
function f_TC_attach_pdp_act_user_addr_itu_x213_ipv4len(integer imsi_suffix, octetstring padding) runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	var BSSGP_ConnHdlrPars pars;
	f_init();
	f_sleep(1.0);
	f_vty_iu_rab_assign_addr_enc(true);
	pars := f_new_BSSGP_ConnHdlrPars(imsi_suffix, f_cell_ids_from_gb(g_gb));
	pars.ranap_exp_itu_x213_addr_format := true;
	pars.ranap_use_itu_x213_addr_format := true;
	pars.ranap_itu_x213_addr_format_padding := padding;
	vc_conn := f_start_handler_pars(refers(f_TC_attach_pdp_act_user), testcasename(), g_gb, pars);
	vc_conn.done;
	f_cleanup();
}
testcase TC_attach_pdp_act_user_addr_itu_x213_ipv4len7() runs on test_CT {
	f_TC_attach_pdp_act_user_addr_itu_x213_ipv4len(1055, ''O)
}
testcase TC_attach_pdp_act_user_addr_itu_x213_ipv4len20() runs on test_CT {
	f_TC_attach_pdp_act_user_addr_itu_x213_ipv4len(1056, '00000000000000000000000000'O)
}
/* Add one more byte than the maximum allowed, with total len of TLA reaching 21 bytes.
 * Expect SGSN to fail and reject the ActivatePDPCtxReq in progress to UE, and
 * deactivate the already created PDP context against GGSN. */
private function f_TC_attach_pdp_act_user_addr_itu_x213_ipv4lentoolarge(charstring id) runs on BSSGP_ConnHdlr {
	var integer ran_index := 3;
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));
	timer T := 5.0;
	apars.exp_rej_cause:= '26'O; /* Network failure */

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := ran_index);
	f_service_request(apars, ran_index := ran_index);
	f_pdp_tx_ctx_act(apars, ran_index := ran_index);
	as_ggsn_gtp_ctx_act_req(apars, send_recovery := false);
	T.start;
	alt {
	[] as_ranap_rab_ass_req(apars) {
		as_pdp_ctx_act_iu(apars, ran_index := ran_index);
		as_ggsn_gtp_ctx_del_req(apars);
	}
	[] T.timeout {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
			"Timeout waiting for RANAP RAB AssReq");
		}
	}
	setverdict(pass);
}
testcase TC_attach_pdp_act_user_addr_itu_x213_ipv4lentoolarge() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	var BSSGP_ConnHdlrPars pars;
	f_init();
	f_sleep(1.0);
	pars := f_new_BSSGP_ConnHdlrPars(1057, f_cell_ids_from_gb(g_gb));
	pars.ranap_use_itu_x213_addr_format := true;
	pars.ranap_itu_x213_addr_format_padding := '0000000000000000000000000000'O;
	vc_conn := f_start_handler_pars(refers(f_TC_attach_pdp_act_user_addr_itu_x213_ipv4lentoolarge), testcasename(), g_gb, pars);
	vc_conn.done;
	f_cleanup();
}

/* Test UE going to PMM IDLE state after having activated the PDP context.
 * SGSN is expected to update the GGSN cancelling the Direct Tunnel feature. */
private function f_TC_attach_pdp_act_pmm_idle(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);

	f_service_request(apars, ran_index := 3);

	f_pdp_ctx_act(apars, ran_index := 3);

	f_iu_release_req(ts_RanapCause_radio_conn_release);
	/* Now UE is in PMM IDLE state at the SGSN.
	 * Expect SGSN to point GTPU at GGSN back to itself: */
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := false, ran_index := 3);

	/* Now UE tries to send new data after a while of being IDLE.
	 * SGSN recreates the Iu ctx and recovers the Direct Tunnel RNC<->GGSN: */
	f_service_request(apars, service_type := SERVICE_TYPE_Data, pdp_status := '2000'O /*NSAPI=5*/, ran_index := 3);
	as_ranap_rab_ass_req(apars);
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := true, ran_index := 3);

	/* Now UE is in PMM ENABLED state, tear it down to clean up: */
	f_pdp_ctx_deact_mo(apars, '00'O, ran_index := 3);
	setverdict(pass);
}
testcase TC_attach_pdp_act_pmm_idle() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_pmm_idle), testcasename(), g_gb, 1005);
	vc_conn.done;
	f_cleanup();
}

/* TODO: same as TC_attach_pdp_act_pmm_idle, but with pdp_status=0000, meaning UE lost status about previous PDP Ctx.
 * We should see SGSN doing DeletePDPCtxReq towards GGSN instead of UdatePDPCtxReq, then probably re-create it. */
private function f_TC_attach_pdp_act_pmm_idle_lost_pdp_status(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);

	f_service_request(apars, ran_index := 3);

	f_pdp_ctx_act(apars, ran_index := 3);

	f_iu_release_req(ts_RanapCause_radio_conn_release);
	/* Now UE is in PMM IDLE state at the SGSN.
	 * Expect SGSN to point GTPU at GGSN back to itself: */
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := false, ran_index := 3);

	/* Now UE tries to send new data after a while of being IDLE.
	 * It forgot its previous pdp status, hence := 0000.
	 * SGSN will delete previous PDP ctx at the GGSN. */
	f_service_request(apars, pdp_status := '0000'O /*NSAPI=5*/, exp_ggsn_pdp_del := true, ran_index := 3);
	f_pdp_ctx_act(apars, ran_index := 3);

	/* Now UE is in PMM ENABLED state, tear it down to clean up: */
	f_pdp_ctx_deact_mo(apars, '00'O, ran_index := 3);
	setverdict(pass);
}
testcase TC_attach_pdp_act_pmm_idle_lost_pdp_status() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_pmm_idle_lost_pdp_status), testcasename(), g_gb, 1006);
	vc_conn.done;
	f_cleanup();
}

/* Test UE going to PMM IDLE state after having activated the PDP context.
 * SGSN is expected to update the GGSN cancelling the Direct Tunnel feature. */
private function f_TC_pmm_idle_rx_mt_data(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := 3);

	f_service_request(apars, ran_index := 3);

	f_pdp_ctx_act(apars, ran_index := 3);

	f_iu_release_req(ts_RanapCause_radio_conn_release);
	/* Now UE is in PMM IDLE state at the SGSN.
	 * Expect SGSN to point GTPU at GGSN back to itself: */
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := false, ran_index := 3);

	/* Now GGSN receives new MT data and forwards it to SGSN: */
	f_gtp_register_teid('00000000'O, GTP_GGSN_IDX); /* Ease debugging in case SGSN sends ErrorInd */
	f_ran_register_imsi(g_pars.imsi, g_pars.p_tmsi); /* Forward Paging below to this component */
	var octetstring payload := f_rnd_octstring(100);
	f_ggsn_gtpu_send(apars, payload);

	BSSAP.receive(tr_RANAP_Paging(ps_domain, imsi_hex2oct(g_pars.imsi)));

	/* Ue comes back, answering the paging:
	 * SGSN recreates the Iu ctx and recovers the Direct Tunnel RNC<->GGSN: */
	f_service_request(apars, pdp_status := '2000'O /*NSAPI=5*/, ran_index := 3);
	as_ranap_rab_ass_req(apars);
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := true, ran_index := 3);

	/* Now UE is in PMM ENABLED state, tear it down to clean up: */
	f_pdp_ctx_deact_mo(apars, '00'O, ran_index := 3);
	setverdict(pass);
}
testcase TC_pmm_idle_rx_mt_data() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_pmm_idle_rx_mt_data), testcasename(), g_gb, 1007);
	vc_conn.done;
	f_cleanup();
}

/* TS 29.060 7.3.3, 3GPP TS 23.007 15.2: Emulate GGSN sending
UpdatePDPCtx[NSAPI, Direct Tunnel Flags(EI=1)] due to having receiving an Error
Indication from RNC while in Direct Tunnel mode. SGSN should attempt recreation
of the RAB to make sure PDP context is established, since it may be that RNC got
restarted. */
private function f_TC_update_ctx_err_ind_from_ggsn(charstring id) runs on BSSGP_ConnHdlr {
	var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip, mp_ranap_cfg[0].sctp_addr.local_ip_addr));
	var integer ran_index := 3;

	/* first perform regular attach */
	f_gmm_attach(umts_aka_challenge := true, force_gsm_sres := false, ran_index := ran_index);

	f_service_request(apars, ran_index := ran_index);

	f_pdp_ctx_act(apars, ran_index := ran_index);

	/* then transceive a downlink PDU */
	f_gtpu_xceive_mt(apars, f_rnd_octstring(100), ran_index := ran_index);
	/* RNC answers with Error Indication, it probably crashed and lost state: */
	f_gtp_register_teid('00000000'O, GTP_GGSN_IDX); /* Ease debugging in case SGSN sends ErrorInd */
	f_ran_register_imsi(g_pars.imsi, g_pars.p_tmsi); /* Forward Paging below to this component */
	GTP[ran2gtp_idx(ran_index)].send(ts_GTPU_ErrorIndication(ts_GtpPeerU(apars.ggsn_ip_u),
							         0, apars.rnc_tei_u,
								 apars.rnc_ip_u));
	GTP[GTP_GGSN_IDX].receive(tr_GTPU_ErrorIndication(ts_GtpPeerU(apars.rnc_ip_u),
							  apars.rnc_tei_u,
							  apars.rnc_ip_u));

	GTP[GTP_GGSN_IDX].send(ts_GTPC_UpdatePDP_GGSN(ts_GtpPeerC(apars.sgsn_ip_c),
						      apars.sgsn_tei_c, 2,
						      apars.nsapi,
						      g_pars.imsi,
						      dtf := ts_DirectTunnelFlags(eI := '1'B)));

	GTP[GTP_GGSN_IDX].receive(tr_GTPC_UpdatePdpRespSGSN(ts_GtpPeerC(apars.sgsn_ip_c), 2,
							    apars.ggsn_tei_c,
							    GTP_CAUSE_REQUEST_ACCEPTED));

	BSSAP.receive(tr_MSC_CONN_PRIM_DISC_IND);
	g_pars.rnc_send_initial_ue := true;
	BSSAP.receive(tr_RANAP_Paging(ps_domain, imsi_hex2oct(g_pars.imsi)));

	/* Ue comes back, answering the paging:
	 * SGSN recreates the Iu ctx and recovers the Direct Tunnel RNC<->GGSN: */
	f_service_request(apars, pdp_status := '2000'O /*NSAPI=5*/, ran_index := ran_index);
	as_ranap_rab_ass_req(apars);
	as_ggsn_gtp_ctx_upd_req(apars, exp_dir_tun := true, ran_index := ran_index);

	/* Now UE is in PMM ENABLED state, tear it down to clean up: */
	f_pdp_ctx_deact_mo(apars, '00'O, ran_index := ran_index);
	setverdict(pass);
}
testcase TC_update_ctx_err_ind_from_ggsn() runs on test_CT {
	var BSSGP_ConnHdlr vc_conn;
	f_init();
	f_sleep(1.0);
	vc_conn := f_start_handler(refers(f_TC_update_ctx_err_ind_from_ggsn), testcasename(), g_gb, 1004);
	vc_conn.done;
	f_cleanup();
}

control {
	execute( TC_iu_attach() );
	execute( TC_iu_attach_encr() );
	execute( TC_iu_attach_geran_rau() );
	execute( TC_geran_attach_iu_rau() );
	execute( TC_attach_pdp_act_user() );
	execute( TC_attach_pdp_act_user_addr_v4raw() );
	execute( TC_attach_pdp_act_user_addr_itu_x213_ipv4len7() );
	execute( TC_attach_pdp_act_user_addr_itu_x213_ipv4len20() );
	execute( TC_attach_pdp_act_user_addr_itu_x213_ipv4lentoolarge() );
	execute( TC_attach_pdp_act_pmm_idle() );
	execute( TC_pmm_idle_rx_mt_data() );
	execute( TC_update_ctx_err_ind_from_ggsn() );
}


}