pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/42241?usp=email )
Change subject: 5gc: Introduce test TC_handover_inter_ngran_xn ......................................................................
5gc: Introduce test TC_handover_inter_ngran_xn
Change-Id: I4de3f0a02ddbccc85988754a4cd83fe67b1453dc --- M 5gc/C5G_Tests.ttcn M 5gc/ConnHdlr.ttcn M 5gc/expected-results.xml M library/NGAP_Emulation.ttcn M library/NGAP_Functions.ttcn M library/ngap/NGAP_EncDec.cc M library/ngap/NGAP_Templates.ttcn M library/ngap/NGAP_Types.ttcn 8 files changed, 228 insertions(+), 16 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/41/42241/1
diff --git a/5gc/C5G_Tests.ttcn b/5gc/C5G_Tests.ttcn index 3844f95..028178d 100644 --- a/5gc/C5G_Tests.ttcn +++ b/5gc/C5G_Tests.ttcn @@ -613,6 +613,69 @@ 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_ping4(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_ip := ho_xn_pars.sess_pars.ue_ip; + 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_ping4(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; +} + control { execute( TC_ng_setup() ); execute( TC_ng_setup_unknown_global_gnb_id_plmn() ); @@ -638,6 +701,8 @@ execute( TC_connection_inactive() ); execute( TC_connection_suspend() );
+ execute( TC_handover_inter_ngran_xn() ); + }
/* TODO: diff --git a/5gc/ConnHdlr.ttcn b/5gc/ConnHdlr.ttcn index ce7d38f..da0d3d9 100644 --- a/5gc/ConnHdlr.ttcn +++ b/5gc/ConnHdlr.ttcn @@ -49,8 +49,8 @@ import from GTPv1U_Emulation all; import from UECUPS_Types all;
-/* (maximum) number of emulated eNBs */ -const integer NUM_NGRAN := 1; +/* (maximum) number of emulated gNBs */ +const integer NUM_NGRAN := 2;
/* parameters of emulated gNB / ng-eNB */ type record NGRANParams { @@ -112,7 +112,25 @@ boolean exp_CoreNetworkAssistanceInformationForInactive }
+/* Kind of 3GPP TS 38.423 9.1.1.1 HANDOVER REQUEST plus + * 3GPP TS 38.423 8.2.4 RETRIEVE UE CONTEXT RESPONSE (see 9.1.1.9 and 9.2.1.13 too). */ +type record Handover_Xn_Pars { + AMF_UE_NGAP_ID source_amf_id, + octetstring guti, + ProcedureTransactionIdentifier pti, + PDUSessionParams sess_pars, + NAS_KeySetIdentifier kset_id, + NG_NAS_UE_State nus /* 3GPP TS 38.423 9.2.1.13 UE Context Information */ +}; + +type port ConnHdlr_Coord_PT message +{ + inout charstring, Handover_Xn_Pars; +} with { extension "internal" }; + type component ConnHdlr extends NGAP_ConnHdlr, GTP1U_ConnHdlr { + port ConnHdlr_Coord_PT COORD; + var ConnHdlrPars g_pars; var UeDerivedKeys g_keys;
@@ -217,6 +235,16 @@ return valueof(st); }
+function f_UESecurityCapabilities() runs on ConnHdlr return UESecurityCapabilities +{ + var template (value) UESecurityCapabilities cap; + cap := m_uESecurityCapabilities(p_nRencryptionAlgorithms := '0000000000000000'B, + p_nRintegrityProtectionAlgorithms := '0000000000000000'B, + p_eUTRAencryptionAlgorithms := '0000000000000000'B, + p_eUTRAintegrityProtectionAlgorithms := '0000000000000000'B); + return valueof(cap); +} + /* 3GPP TS 24.501 5.4.1.3.2, 3GPP TS 33.501 6.1.3.2 */ private altstep as_ngap_handle_auth(boolean allow_resync := true) runs on ConnHdlr { var NG_NAS_DL_Message_Type rx_nas; @@ -881,6 +909,43 @@ g_pars.ue_pars.ran_id))); }
+/* 3GPP TS 38.413 8.4.4 Path Switch Request + * 3GPP TS 38.413 9.2.3.8 PATH SWITCH REQUEST + * 3GPP TS 23.502 4.9.1.2.2 Xn based inter NG-RAN handover without User Plane function re-allocation */ +function f_n2_path_switch(AMF_UE_NGAP_ID source_amf_id) runs on ConnHdlr +{ + var template (value) UPTransportLayerInformation utla; + var template (value) PathSwitchRequestTransfer transfer; + var octetstring transfer_enc; + var template (value) PDUSessionResourceToBeSwitchedDLItem it; + var template (value) NGAP_PDU tx_pdu; + var NGAP_PDU rx_pdu; + + utla := m_uPTransportLayerInformation_gTPTunnel( + m_gTPTunnel(p_tla := oct2bit(f_inet_addr(g_pars.ue_pars.sess_pars.ran_gtpu_ip)), + p_gtp_teid := g_pars.ue_pars.sess_pars.ran_gtpu_teid)); + transfer := m_pathSwitchRequestTransfer(p_dL_NGU_UP_TNLInformation := utla, + p_qosFlowAcceptedList := { m_qosFlowAcceptedItem(1) }, + p_dL_NGU_TNLInformationReused := omit, + p_userPlaneSecurityInformation := omit); + transfer_enc := enc_NGAP_PathSwitchRequestTransfer(valueof(transfer)); + it := m_pDUSessionResourceToBeSwitchedDLItem(g_pars.ue_pars.sess_pars.id, + transfer_enc); + + tx_pdu := m_ngap_initMsg(m_n2_PathSwitchRequest( + g_pars.ue_pars.ran_id, + source_amf_id, + f_ULI(), + f_UESecurityCapabilities(), + { it })); + NGAP.send(tx_pdu); + NGAP.receive(mw_ngap_succMsg(mw_n2_PathSwitchRequestAcknowledge(?, g_pars.ue_pars.ran_id))) -> value rx_pdu; + + /* Update amf_id: */ + var PathSwitchRequestAcknowledge path_switch_ack := rx_pdu.successfulOutcome.value_.PathSwitchRequestAcknowledge; + g_pars.ue_pars.amf_id := path_switch_ack.protocolIEs[0].value_.aMF_UE_NGAP_ID; +} + /* Handle a PDUSessionResourceSetupRequestTransfer contained inside NGAP InitialContextSetupRequest and return a Result for InitialContextSetupResponse */ private function f_pdu_handle_session_resource_released_item(PDUSessionResourceToReleaseItemRelCmd cmd) runs on ConnHdlr return PDUSessionResourceReleasedItemRelRes { diff --git a/5gc/expected-results.xml b/5gc/expected-results.xml index 22a18be..2333f8d 100644 --- a/5gc/expected-results.xml +++ b/5gc/expected-results.xml @@ -23,4 +23,5 @@ C5G_Tests.ttcn:MASKED TC_connection_suspend testcase </failure> </testcase> + <testcase classname='C5G_Tests' name='TC_handover_inter_ngran_xn' time='MASKED'/> </testsuite> diff --git a/library/NGAP_Emulation.ttcn b/library/NGAP_Emulation.ttcn index 22eb605..d050d2f 100644 --- a/library/NGAP_Emulation.ttcn +++ b/library/NGAP_Emulation.ttcn @@ -117,12 +117,16 @@ signature NGAPEM_register(in AMF_UE_NGAP_ID amf_id, in RAN_UE_NGAP_ID ran_id, in NGAP_ConnHdlr hdlr); signature NGAPEM_register_proc(in integer procedureCode, in NGAP_ConnHdlr hdlr); signature NGAPEM_obtain_amf_id() return AMF_UE_NGAP_ID; +signature NGAPEM_obtain_nus(in RAN_UE_NGAP_ID ran_id) return NG_NAS_UE_State; +signature NGAPEM_set_nus(in RAN_UE_NGAP_ID ran_id, in NG_NAS_UE_State nus); //signature NGAPEM_derive_nas_token(in octetstring kasme, in NGAP_ConnHdlr hdlr, out OCT32 nas_token);
type port NGAPEM_PROC_PT procedure { inout NGAPEM_register; inout NGAPEM_register_proc; inout NGAPEM_obtain_amf_id; + inout NGAPEM_obtain_nus; + inout NGAPEM_set_nus; //inout NGAPEM_derive_nas_token; } with { extension "internal" };
@@ -288,13 +292,18 @@ return -1; /* make ttcn3 compiler happy */ }
-private function f_assoc_id_by_comp(NGAP_ConnHdlr client) +private function f_assoc_id_by_comp(NGAP_ConnHdlr client, template (omit) RAN_UE_NGAP_ID ran_id := omit) runs on NGAP_Emulation_CT return integer { var integer i; for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) { - if (NGapAssociationTable[i].comp_ref == client) { - return i; + if (NGapAssociationTable[i].comp_ref != client) { + continue; } + if (not istemplatekind(ran_id, "omit") and + not match(NGapAssociationTable[i].ran_ue_ngap_id, ran_id)) { + continue; + } + return i; } setverdict(fail, "NGAP Association Table not found by component ", client); mtc.stop; @@ -481,6 +490,7 @@ var charstring vlr_name, amf_name; var integer ai; var octetstring kasme; + var NG_NAS_UE_State nus;
alt { /* Configuration primitive from client */ @@ -518,6 +528,15 @@ /* Pass message through */ NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg)); } + /* NGAP from client: PathSwitch */ + [] NGAP_CLIENT.receive(mw_ngap_initMsg(mw_n2_PathSwitchRequest)) -> value msg sender vc_conn { + /* create a table entry about this connection */ + ai := f_ngap_id_table_add(vc_conn, omit, valueof(f_NGAP_get_RAN_UE_NGAP_ID(msg))); + /* Store ULI so we can use it for generating UlNasTransport from NAS */ + NGapAssociationTable[ai].uli := msg.initiatingMessage.value_.PathSwitchRequest.protocolIEs[2].value_.userLocationInformation; + /* Pass message through */ + NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg)); + } /* NAS from client: Wrap in NGAP Uplink NAS Transport */ [] NGAP_CLIENT.receive(NG_NAS_UL_Message_Type:?) -> value ul_nas_msg sender vc_conn { var integer assoc_id := f_assoc_id_by_comp(vc_conn); @@ -612,6 +631,15 @@ amf_id := NGapAssociationTable[i].amf_ue_ngap_id; NGAP_PROC.reply(NGAPEM_obtain_amf_id:{} value amf_id) to vc_conn; } + [] NGAP_PROC.getcall(NGAPEM_obtain_nus:{?}) -> param(ran_id) sender vc_conn { + var integer i := f_assoc_id_by_comp(vc_conn, ran_id); + NGAP_PROC.reply(NGAPEM_obtain_nus:{ran_id} value NGapAssociationTable[i].nus) to vc_conn; + } + [] NGAP_PROC.getcall(NGAPEM_set_nus:{?, ?}) -> param(ran_id, nus) sender vc_conn { + var integer i := f_assoc_id_by_comp(vc_conn, ran_id); + NGapAssociationTable[i].nus := nus; + NGAP_PROC.reply(NGAPEM_set_nus:{ran_id, nus}) to vc_conn; + } // [] NGAP_PROC.getcall(NGAPEM_derive_nas_token:{?, ?, -}) -> param(kasme, vc_conn) { // var integer assoc_id := f_assoc_id_by_comp(vc_conn); // var OCT32 nas_token := f_kdf_nas_token(kasme, NGapAssociationTable[assoc_id].nus.tx_count) @@ -721,4 +749,19 @@ } }
+function f_ngap_obtain_nus(RAN_UE_NGAP_ID ran_id) runs on NGAP_ConnHdlr return NG_NAS_UE_State { + var NG_NAS_UE_State nus; + NGAP_PROC.call(NGAPEM_obtain_nus:{ran_id}) { + [] NGAP_PROC.getreply(NGAPEM_obtain_nus:{ran_id}) -> value nus { + return nus; + } + } +} + +function f_ngap_set_nus(RAN_UE_NGAP_ID ran_id, NG_NAS_UE_State nus) runs on NGAP_ConnHdlr { + NGAP_PROC.call(NGAPEM_set_nus:{ran_id, nus}) { + [] NGAP_PROC.getreply(NGAPEM_set_nus:{ran_id, nus}); + } +} + } diff --git a/library/NGAP_Functions.ttcn b/library/NGAP_Functions.ttcn index e1fcf6d..5d45c43 100644 --- a/library/NGAP_Functions.ttcn +++ b/library/NGAP_Functions.ttcn @@ -45,6 +45,9 @@ } else if (ischosen(ngap.successfulOutcome)) { var SuccessfulOutcome so := ngap.successfulOutcome; select (so.procedureCode) { + case (id_PathSwitchRequest) { + return so.value_.PathSwitchRequestAcknowledge.protocolIEs[0].value_.aMF_UE_NGAP_ID; + } case (?) { return omit; /* TODO */ @@ -92,11 +95,17 @@ case (id_PDUSessionResourceRelease) { return im.value_.pDUSessionResourceReleaseCommand.protocolIEs[1].value_.rAN_UE_NGAP_ID; } + case (id_PathSwitchRequest) { + return im.value_.PathSwitchRequest.protocolIEs[0].value_.RAN_UE_NGAP_ID; + } /* TODO */ } } else if (ischosen(ngap.successfulOutcome)) { var SuccessfulOutcome so := ngap.successfulOutcome; select (so.procedureCode) { + case (id_PathSwitchRequest) { + return so.value_.PathSwitchRequestAcknowledge.protocolIEs[1].value_.rAN_UE_NGAP_ID; + } case (?) { return omit; /* TODO */ diff --git a/library/ngap/NGAP_EncDec.cc b/library/ngap/NGAP_EncDec.cc index 8b825d5..9e3ccd2 100644 --- a/library/ngap/NGAP_EncDec.cc +++ b/library/ngap/NGAP_EncDec.cc @@ -129,4 +129,24 @@ return ret; }
+OCTETSTRING enc__NGAP__PathSwitchRequestTransfer(const NGAP__IEs::PathSwitchRequestTransfer &p) +{ + TTCN_Buffer TTCN_buf; + TTCN_buf.clear(); + p.encode(NGAP__IEs::PathSwitchRequestTransfer_descr_, TTCN_buf, + TTCN_EncDec::CT_PER, PER_ALIGNED); + return OCTETSTRING(TTCN_buf.get_len(), TTCN_buf.get_data()); +} + +NGAP__IEs::PathSwitchRequestTransfer dec__NGAP__PathSwitchRequestTransfer(const OCTETSTRING &stream) +{ + NGAP__IEs::PathSwitchRequestTransfer ret; + TTCN_Buffer TTCN_buf; + TTCN_buf.clear(); + TTCN_buf.put_os(stream); + ret.decode(NGAP__IEs::PathSwitchRequestTransfer_descr_, TTCN_buf, + TTCN_EncDec::CT_PER, PER_ALIGNED); + return ret; +} + } diff --git a/library/ngap/NGAP_Templates.ttcn b/library/ngap/NGAP_Templates.ttcn index 0931687..6626cc1 100644 --- a/library/ngap/NGAP_Templates.ttcn +++ b/library/ngap/NGAP_Templates.ttcn @@ -4891,16 +4891,16 @@ PathSwitchRequest := { protocolIEs := { { - id := id_AMF_UE_NGAP_ID, - criticality := reject, - value_ := { AMF_UE_NGAP_ID := p_amfUeNgapID } - }, - { id := id_RAN_UE_NGAP_ID, criticality := reject, value_ := { RAN_UE_NGAP_ID := p_ranUeNgapID } }, { + id := id_SourceAMF_UE_NGAP_ID, + criticality := reject, + value_ := { AMF_UE_NGAP_ID := p_amfUeNgapID } + }, + { id := id_UserLocationInformation, criticality := ignore, value_ := { UserLocationInformation := p_userLocationInformation } @@ -4940,16 +4940,16 @@ PathSwitchRequest := { protocolIEs := { { - id := id_AMF_UE_NGAP_ID, - criticality := reject, - value_ := { AMF_UE_NGAP_ID := p_amfUeNgapID } - }, - { id := id_RAN_UE_NGAP_ID, criticality := reject, value_ := { RAN_UE_NGAP_ID := p_ranUeNgapID } }, { + id := id_SourceAMF_UE_NGAP_ID, + criticality := reject, + value_ := { AMF_UE_NGAP_ID := p_amfUeNgapID } + }, + { id := id_UserLocationInformation, criticality := ignore, value_ := { UserLocationInformation := p_userLocationInformation } @@ -5027,7 +5027,8 @@ template (present) AMF_UE_NGAP_ID p_amfUeNgapID := ?, template (present) RAN_UE_NGAP_ID p_ranUeNgapID := ?, template (present) SecurityContext p_securityContext := ?, - template (present) PDUSessionResourceSwitchedList p_pDUSessionResourceSwitchedList := ? + template (present) PDUSessionResourceSwitchedList p_pDUSessionResourceSwitchedList := ?, + template (present) AllowedNSSAI p_allowedNSSAI := ? ) := { procedureCode := id_PathSwitchRequest, criticality := reject, @@ -5053,6 +5054,11 @@ id := id_PDUSessionResourceSwitchedList, criticality := ignore, value_ := { PDUSessionResourceSwitchedList := p_pDUSessionResourceSwitchedList } + }, + { + id := id_AllowedNSSAI, + criticality := reject, + value_ := { AllowedNSSAI := p_allowedNSSAI } } } } diff --git a/library/ngap/NGAP_Types.ttcn b/library/ngap/NGAP_Types.ttcn index d9523b4..394cb24 100644 --- a/library/ngap/NGAP_Types.ttcn +++ b/library/ngap/NGAP_Types.ttcn @@ -20,4 +20,7 @@
external function enc_NGAP_UEContextSuspendRequestTransfer(NGAP_IEs.UEContextSuspendRequestTransfer p) return octetstring; external function dec_NGAP_UEContextSuspendRequestTransfer(in octetstring pdu) return NGAP_IEs.UEContextSuspendRequestTransfer; + + external function enc_NGAP_PathSwitchRequestTransfer(NGAP_IEs.PathSwitchRequestTransfer p) return octetstring; + external function dec_NGAP_PathSwitchRequestTransfer(in octetstring pdu) return NGAP_IEs.PathSwitchRequestTransfer; }