/* 5GC (55G Core) test suite in TTCN-3, ConnHdlr * (C) 2025 by sysmocom - s.f.m.c. GmbH * All rights reserved. * Author: Pau Espin Pedrol * * 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 ConnHdlr { 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 UECUPS_Types all; /* (maximum) number of emulated eNBs */ const integer NUM_NGRAN := 1; /* parameters of emulated gNB / ng-eNB */ type record NGRANParams { GlobalRANNodeID global_ngran_id, NGRAN_CGI cell_identity, SupportedTAList supported_ta_list } type record UeDerivedKeys { OCT32 kausf, OCT32 kseaf, OCT32 kamf }; type record PDUSessionParams { PDUSessionID id, RunProgParams run_prog_pars, charstring ran_gtpu_ip, OCT4 ran_gtpu_teid, charstring cn_gtpu_ip optional, OCT4 cn_gtpu_teid optional, QoS_RuleList qos_rules optional, charstring ue_ip optional }; /* parameters of emulated UE */ type record UeParams { integer idx, HEX15n imsi, HEX16n imeisv, octetstring usim_key, octetstring usim_opc, charstring apn, RAN_UE_NGAP_ID ran_id, AMF_UE_NGAP_ID amf_id optional, octetstring guti optional, ProcedureTransactionIdentifier pti, PDUSessionParams sess_pars }; type record RunProgParams { charstring run_as_user, charstring log_path_prefix, charstring tun_netns_name, charstring tun_dev_name, charstring ping_hostname }; type record ConnHdlrPars { /* copied over from MTC_CT on start of component */ NGRANParams ngran_pars[NUM_NGRAN], /* copied over from MTC_CT on start of component */ UeParams ue_pars, /* currently used 5GC (index into ngran_pars, NGAP, ...) */ integer c5g_idx, /* Currently set KSI */ NAS_KeySetIdentifier kset_id } type component ConnHdlr extends NGAP_ConnHdlr, GTP1U_ConnHdlr { var ConnHdlrPars g_pars; var UeDerivedKeys g_keys; /* number of programs started, used as identifier. */ var integer g_start_prog_count := 0; } type function void_fn() runs on ConnHdlr; private template (value) NGAP_IEs.TAI ts_ngran_NGAP_TAI(NGRANParams ngran_pars) := { pLMNIdentity := ngran_pars.global_ngran_id.globalGNB_ID.pLMNIdentity, tAC := ngran_pars.supported_ta_list[0].tAC, iE_Extensions := omit } private function f_imsi_plmn_id() runs on ConnHdlr return PLMNIdentity { var hexstring imsi := g_pars.ue_pars.imsi; var GsmMcc mcc := substr(imsi, 0, 3); var GsmMnc mnc := substr(imsi, 3, 2); return f_enc_mcc_mnc(mcc, mnc); } private function f_SUCI_IMSI() runs on ConnHdlr return octetstring { var hexstring imsi := g_pars.ue_pars.imsi; var PLMNIdentity plmn_id := f_imsi_plmn_id(); var octetstring imsi_suffix := imsi_hex2oct(substr(imsi, lengthof(imsi) - 10, 10)); return plmn_id & '21430001'O & imsi_suffix; } private function f_UE_SecurityCapability() runs on ConnHdlr return NG_UE_SecurityCapability { var template (value) NG_UE_SecurityCapability ue_sec_cap; ue_sec_cap := cs_NG_UE_SecurityCapabilityTLV(ngeaCap := '80'O, /* ONLY NEA0 (no encryption) for now */ ngiaCap := '40'O /* ONLY NIA1 supported */); return valueof(ue_sec_cap); } private function f_ULI() runs on ConnHdlr return UserLocationInformation { var template (value) UserLocationInformation p_ueLocInf p_ueLocInf := m_uPTransportLayerInformation_userLocationInformationNR( m_userLocationInformationNR( g_pars.ngran_pars[g_pars.c5g_idx].cell_identity.nR_CGI, ts_ngran_NGAP_TAI(g_pars.ngran_pars[g_pars.c5g_idx]) )); return valueof(p_ueLocInf); } private function f_tr_ConnHdlr_kset_id() runs on ConnHdlr return template (present) NAS_KeySetIdentifier { /* KSI not yet set, expect whatever assignment from network: */ if (g_pars.kset_id.nasKeySetId == tsc_NasKsi_NoKey) { return cr_NAS_KeySetIdentifier(?, ?); } else { /* Expect specific kset_id. Upon transmitting it UE->Network, f_attach() * has updated it to the expected value. */ return g_pars.kset_id; } } /* 3GPP TS 24.501 9.6, 3GPP TS 24.007 11.2.3.1a. * 0 = "No procedure transaction identity assigned", 255 = "Reserved". */ private function f_next_pti() runs on ConnHdlr return ProcedureTransactionIdentifier { var integer i := oct2int(g_pars.ue_pars.pti); if (i >= 254) { g_pars.ue_pars.pti := '01'O; } else { g_pars.ue_pars.pti := int2oct(i + 1, 1); } return g_pars.ue_pars.pti; } /* 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; var template (present) NAS_KeySetIdentifier kset_id := f_tr_ConnHdlr_kset_id(); [] NGAP.receive(cr_NG_AUTHENTICATION_REQUEST) -> value rx_nas { var integer ret; var integer my_sqn := 0; /* TODO: move to a ConnHdlr state attribute? */ var OCT16 rand := bit2oct(rx_nas.authentication_Request.rand.randValue); var OCT16 autn := bit2oct(rx_nas.authentication_Request.autn.aUTN); var OCT2 abba := rx_nas.authentication_Request.abba.abbaValue; var OCT16 ik; var OCT16 ck; var OCT8 res; var OCT14 auts; g_pars.kset_id := rx_nas.authentication_Request.ngNasKeySetId; ret := f_milenage_check(g_pars.ue_pars.usim_key, g_pars.ue_pars.usim_opc, int2oct(my_sqn, 8), rand, autn, ik, ck, res, auts); if (ret == -1) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("fx_milenage_check() failed! -1")); } if (ret == -2) { if (not allow_resync) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("fx_milenage_check() unexpected Resync!")); } log("Milenage: RX-SQN differs from SIM SQN: Re-Sync! AUTS=", auts); /* 3GPP TS 24.501 5.4.1.3.6 d) */ NGAP.send(cs_NG_AUTHENTICATION_FAILURE(cs_GMM_GSM_Cause(omit, int2bit(21, 8)), cs_AuthFailParam(oct2bit(auts)))); as_ngap_handle_auth(allow_resync := false); return; } var octetstring ssn := f_NG_NAS_ServingNetworkName_OCT(f_imsi_plmn_id(), omit); g_keys.kausf := f_kdf_kausf(ck, ik, ssn, autn); g_keys.kseaf := f_kdf_kseaf(g_keys.kausf, ssn); g_keys.kamf := f_kdf_kamf(g_keys.kseaf, char2oct(hex2str(g_pars.ue_pars.imsi)), abba); var NGAPEM_Config cfg := { set_nas_keys := { k_nas_int := f_kdf_ng_nas_int(g_keys.kamf, NG_NAS_ALG_IP_NIA1), k_nas_enc := f_kdf_ng_nas_enc(g_keys.kamf, NG_NAS_ALG_ENC_NEA0) } }; NGAP.send(cfg); /* Derive (X)RES* from (X)RES, 3GPP TS 33.501 A.4 */ var OCT16 res_star := f_kdf_xres_star(ssn, ck, ik, rand, res); NGAP.send(cs_NG_AUTHENTICATION_RESPONSE(cs_AuthenticationResponseParameter(oct2bit(res_star)))); } } private altstep as_ngap_handle_sec_mode() runs on ConnHdlr { var NGAPEM_Config cfg; var NG_NAS_DL_Message_Type rx_nas; /* Make sure network selected specific algos we requested: */ var template (present) NG_UE_SecurityCapability ue_sec_cap := cr_NG_UE_SecurityCapabilityLV(ngeaCap := '80'O, /* ONLY NEA0 (no encryption) for now */ ngiaCap := '40'O /* ONLY NIA1 supported */); [] NGAP.receive(cr_NG_SECURITY_MODE_COMMAND(p_UECap := ue_sec_cap)) -> value rx_nas { /* Configure integrity protection: */ cfg := { set_nas_alg_int := NG_NAS_ALG_IP_NIA1 }; NGAP.send(cfg); /* Configure Ciphering: */ cfg := { set_nas_alg_enc := NG_NAS_ALG_ENC_NEA0 }; NGAP.send(cfg); var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NAS_PDU nas_pdu; nas_ul_msg := cs_NG_REGISTRATION_REQUEST(cs_RegistrationType(tsc_NG_RegistrationInitial, '1'B), g_pars.kset_id.nasKeySetId, g_pars.kset_id.tsc, cs_NG_MobileIdentitySUCI('0000'B /* Type IMSI */, f_SUCI_IMSI()), p_UESecurityCap := f_UE_SecurityCapability()); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); NGAP.send(cs_NG_SECURITY_MODE_COMPLETE(cs_NG_MobileIdentityTLV_IMEISV(hex2oct(g_pars.ue_pars.imeisv)), cs_ReplayedNASMessageContainerTLV(nas_pdu))); } } private altstep as_ngap_handle_configuration_update() runs on ConnHdlr { var NG_NAS_DL_Message_Type rx_nas; [] NGAP.receive(cr_NG_CONFIGURATION_UPDATE_COMMAND) -> value rx_nas { NGAP.send(cs_NG_CONFIGURATION_UPDATE_COMPLETE); } } /* Handle a PDUSessionResourceSetupRequestTransfer contained inside NGAP InitialContextSetupRequest and return a Result for InitialContextSetupResponse */ private function f_pdu_handle_session_resource_setup_item(PDUSessionResourceSetupItemCxtReq req) runs on ConnHdlr return PDUSessionResourceSetupItemCxtRes { var PDUSessionResourceSetupItemCxtRes resp; var octetstring setup_trans_enc; var NGAP_IEs.PDUSessionResourceSetupRequestTransfer setup_req_transf; var NGAP_IEs.PDUSessionResourceSetupResponseTransfer setup_resp_transf; var template (value) UPTransportLayerInformation utla; var template (value) QosFlowPerTNLInformation qos; /* Parse PDUSessionResourceSetupRequestTransfer contained inside InitialContextSetupRequest's PDUSessionResourceSetup Item: */ setup_req_transf := dec_NGAP_PDUSessionResourceSetupRequestTransfer(req.pDUSessionResourceSetupRequestTransfer); for (var integer i := 0; i < lengthof(setup_req_transf.protocolIEs); i := i + 1) { if (setup_req_transf.protocolIEs[i].id == id_UL_NGU_UP_TNLInformation) { var UPTransportLayerInformation utli := setup_req_transf.protocolIEs[i].value_.uPTransportLayerInformation; g_pars.ue_pars.sess_pars.cn_gtpu_ip := f_inet_ntoa(bit2oct(utli.gTPTunnel.transportLayerAddress)); g_pars.ue_pars.sess_pars.cn_gtpu_teid := utli.gTPTunnel.gTP_TEID; } } /* Prepare Response for it: */ 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)); qos := m_qosFlowPerTNLInformation(utla, { m_associatedQosFlowItem(1) }); setup_resp_transf := valueof(m_pDUSessionResourceSetupResponseTransfer(qos)); setup_trans_enc := enc_NGAP_PDUSessionResourceSetupResponseTransfer(setup_resp_transf) resp := valueof(m_pDUSessionResourceSetupItemCxtRes(req.pDUSessionID, setup_trans_enc)); return resp; } private function f_pdu_handle_session_resource_setup_list(PDUSessionResourceSetupListCxtReq li_req) runs on ConnHdlr return PDUSessionResourceSetupListCxtRes { var PDUSessionResourceSetupListCxtRes li_resp; for (var integer i := 0; i < lengthof(li_req); i := i + 1) { var PDUSessionResourceSetupItemCxtRes it_resp; it_resp := f_pdu_handle_session_resource_setup_item(li_req[i]); if (i == 0) { /* min 1 item in list doesn't allow previously allocating an empty list */ li_resp := { it_resp }; } else { li_resp := li_resp & { it_resp }; } } return li_resp; } /* 3GPP TS 23.502 Figure 4.3.2.2.1-1 steps 12-14. */ private altstep as_ngap_handle_InitialCtxReq_withPDUSessionList() runs on ConnHdlr { var NGAP_PDU rx_ngap; var template (present) NGAP_PDU exp_ngap := mw_ngap_initMsg(f_mw_n2_InitialContextSetupRequest(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, p_uEAggregateMaximumBitRate := ?, p_pDUSessionResourceSetupListCxtReq := ?, p_nextHopNH := ?, p_maskedIMEISV := ?)); [] NGAP.receive(exp_ngap) -> value rx_ngap { var InitialContextSetupRequest ctx_setup_req := rx_ngap.initiatingMessage.value_.initialContextSetupRequest; var PDUSessionResourceSetupListCxtRes resources; for (var integer i := 0; i < lengthof(ctx_setup_req.protocolIEs); i := i + 1) { if (ctx_setup_req.protocolIEs[i].id != id_PDUSessionResourceSetupListCxtReq) { continue; } var PDUSessionResourceSetupListCxtReq li := ctx_setup_req.protocolIEs[i].value_.pDUSessionResourceSetupListCxtReq; resources := f_pdu_handle_session_resource_setup_list(li); } NGAP.send(m_ngap_succMsg(m_n2_InitialContextSetupResponse(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, resources))); } } /* 3GPP TS 23.502 4.3.2.2 UE Requested PDU Session Establishment */ function f_pdu_sess_establish(boolean configure_userplane := true) runs on ConnHdlr { var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NAS_PDU nas_pdu; var NG_NAS_DL_Message_Type rx_nas, inner_nas; var ProcedureTransactionIdentifier pti := f_next_pti(); var OCT1 pdu_sess_id := int2oct(g_pars.ue_pars.sess_pars.id, 1); nas_ul_msg := cs_NG_PDU_SESSION_ESTABLISHMENT_REQUEST(cs_NG_PDU_SessionIdV(pdu_sess_id), pti, p_IntegrityProtMaxDataRate := cs_IntegrityProtMaxDataRateV('FF'O, 'FF'O), p_PDU_SessionType := cs_PDU_SessionTypeTV('001'B), /* IPv4 */ p_SSC_Mode := cs_SSC_ModeTV('001'B)); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); nas_ul_msg := cs_NG_UL_NAS_TRANSPORT(cs_PayloadContainerType(tsc_PayloadContainerESMMsg), cs_PayloadContainerLV(nas_pdu), p_PDU_SessionId := cs_NG_PDU_SessionIdTV(pdu_sess_id), p_RequestType := cs_NG_Request_TypeTV('001'B), p_DNN := cs_DNN_TLV(f_enc_dns_hostname(g_pars.ue_pars.apn))); NGAP.send(nas_ul_msg); as_ngap_handle_InitialCtxReq_withPDUSessionList(); /* PDU Session Establishment Accept: This DL NAS arrives contained in InitialCtxReq handled above: */ NGAP.receive(cr_NG_DL_NAS_TRANSPORT) -> value rx_nas; inner_nas := f_dec_NG_NAS_DL_Message_Payload_Container(rx_nas.dl_Nas_Transport.payloadContainerType.container, rx_nas.dl_Nas_Transport.payload.payload); g_pars.ue_pars.sess_pars.qos_rules := inner_nas.pdu_Session_Establishment_Accept.authorizedQoSRules.listofQoSRules; g_pars.ue_pars.sess_pars.ue_ip := f_inet_ntoa(inner_nas.pdu_Session_Establishment_Accept.pduAddress.adressInfo); log("5GC assigned CN GTPU Address: ", g_pars.ue_pars.sess_pars.cn_gtpu_ip, " TEID: ", g_pars.ue_pars.sess_pars.cn_gtpu_teid); log("5GC assigned UE IP address: ", g_pars.ue_pars.sess_pars.ue_ip); if (configure_userplane) { f_pdu_sess_create_tun(); } } private function f_pdu_handle_session_resource_modify_item_qos_flow_list(QosFlowAddOrModifyRequestList qos_flow_req_li) runs on ConnHdlr return QosFlowAddOrModifyResponseList { var QosFlowAddOrModifyResponseList qos_flow_resp_li; for (var integer i := 0; i < lengthof(qos_flow_req_li); i := i + 1) { var QosFlowAddOrModifyRequestItem qos_flow_req_it := qos_flow_req_li[i]; var QosFlowAddOrModifyResponseItem qos_flow_resp_it := valueof(m_qosFlowAddOrModifyResponseItem(qos_flow_req_it.qosFlowIdentifier)); qos_flow_resp_li[i] := qos_flow_resp_it; } return qos_flow_resp_li; } /* Handle a PDUSessionResourceModifyRequestTransfer contained inside NGAP PDUSessionResourceModifyRequest and return a Result for PDUSessionResourceModifyResponse */ private function f_pdu_handle_session_resource_modify_item(PDUSessionResourceModifyItemModReq req) runs on ConnHdlr return PDUSessionResourceModifyItemModRes { var PDUSessionResourceModifyItemModRes resp; var NGAP_IEs.PDUSessionResourceModifyRequestTransfer mod_req_transf; var NGAP_IEs.PDUSessionResourceModifyResponseTransfer mod_resp_transf; var octetstring mod_resp_transf_enc; var template (omit) QosFlowAddOrModifyResponseList qos_flow_resp_li := omit; mod_req_transf := dec_NGAP_PDUSessionResourceModifyRequestTransfer(req.pDUSessionResourceModifyRequestTransfer); for (var integer i := 0; i < lengthof(mod_req_transf.protocolIEs); i := i + 1) { select(mod_req_transf.protocolIEs[i].id) { case (id_QosFlowAddOrModifyRequestList) { var QosFlowAddOrModifyRequestList qos_flow_req_li := mod_req_transf.protocolIEs[i].value_.qosFlowAddOrModifyRequestList; qos_flow_resp_li := f_pdu_handle_session_resource_modify_item_qos_flow_list(qos_flow_req_li); } case (id_UL_NGU_UP_TNLModifyList) { // TODO: assign the fields below, see how it's done in f_pdu_handle_session_resource_setup_item() // g_pars.ue_pars.sess_pars.cn_gtpu_ip := ; // g_pars.ue_pars.sess_pars.cn_gtpu_teid := ; // TODO: Update IP+TEID in usperlane osmo-uecups if any of them changed. } } } /* Prepare Response for it: */ mod_resp_transf := valueof(m_pDUSessionResourceModifyResponseTransfer(p_qosFlowAddOrModifyResponseList := qos_flow_resp_li)); mod_resp_transf_enc := enc_NGAP_PDUSessionResourceModifyResponseTransfer(mod_resp_transf); resp := valueof(m_pDUSessionResourceModifyItemModRes(req.pDUSessionID, mod_resp_transf_enc)); return resp; } private function f_pdu_handle_session_resource_modify_list(PDUSessionResourceModifyListModReq li_cmd) runs on ConnHdlr return PDUSessionResourceModifyListModRes { var PDUSessionResourceModifyListModRes li_resp; for (var integer i := 0; i < lengthof(li_cmd); i := i + 1) { var PDUSessionResourceModifyItemModRes it_resp; it_resp := f_pdu_handle_session_resource_modify_item(li_cmd[i]); li_resp[i] := it_resp; } return li_resp; } private altstep as_ngap_handle_PDUSessionResourceModifyRequest() runs on ConnHdlr { var NGAP_PDU rx_ngap; var template (present) NGAP_PDU exp_ngap := mw_ngap_initMsg(mw_n2_PDUSessionResourceModify(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, ?)); [] NGAP.receive(exp_ngap) -> value rx_ngap { var PDUSessionResourceModifyRequest mod_req := rx_ngap.initiatingMessage.value_.PDUSessionResourceModifyRequest; var PDUSessionResourceModifyListModRes resources; for (var integer i := 0; i < lengthof(mod_req.protocolIEs); i := i + 1) { if (mod_req.protocolIEs[i].id != id_PDUSessionResourceModifyListModReq) { continue; } var PDUSessionResourceModifyListModReq li := mod_req.protocolIEs[i].value_.pDUSessionResourceModifyListModReq; resources := f_pdu_handle_session_resource_modify_list(li); } NGAP.send(m_ngap_succMsg(m_n2_PDUSessionResourceModifyResponse(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, resources))); } } /* 3GPP TS 24.501 8.3.7 PDU session modification request, * 3GPP TS 23.502 4.3.3.2 UE requested PDU Session Modification */ function f_pdu_sess_modify() runs on ConnHdlr { var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NAS_PDU nas_pdu; var NG_NAS_DL_Message_Type rx_nas, inner_nas; var ProcedureTransactionIdentifier pti := f_next_pti(); var OCT1 pdu_sess_id := int2oct(g_pars.ue_pars.sess_pars.id, 1); var template (value) QoS_RuleU qos_rule_u := cs_QoS_RuleU_Create( g_pars.ue_pars.sess_pars.qos_rules[0].u.creat.flowIdentifier, '09'O, {cs_PacketFilter_MediaUDP('0110'B, 60350)}, '0'B, g_pars.ue_pars.sess_pars.qos_rules[0].u.creat.spare, '1'B ); var template (value) QoS_RuleList qos_rules := { cs_QoS_Rule(g_pars.ue_pars.sess_pars.qos_rules[0].identifier, qos_rule_u) }; /* 3GPP TS 24.501 Table 8.3.7.1. */ nas_ul_msg := cs_NG_PDU_SESSION_MODIFICATION_REQUEST(cs_NG_PDU_SessionIdV(pdu_sess_id), pti, p_IntegrityProtMaxDataRate := cs_IntegrityProtMaxDataRateTV('FF'O, 'FF'O), p_QoS_Rules := cs_QoS_Rules(qos_rules, p_IEI := '7A'O)); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); nas_ul_msg := cs_NG_UL_NAS_TRANSPORT(cs_PayloadContainerType(tsc_PayloadContainerESMMsg), cs_PayloadContainerLV(nas_pdu), p_PDU_SessionId := cs_NG_PDU_SessionIdTV(pdu_sess_id), p_RequestType := cs_NG_Request_TypeTV('001'B), p_DNN := cs_DNN_TLV(f_enc_dns_hostname(g_pars.ue_pars.apn))); NGAP.send(nas_ul_msg); as_ngap_handle_PDUSessionResourceModifyRequest(); /* 5G-NAS PDU Session Modify Request: This DL NAS arrives contained in NGAP PDUSessionModifyRequest handled above: */ NGAP.receive(cr_NG_DL_NAS_TRANSPORT) -> value rx_nas; inner_nas := f_dec_NG_NAS_DL_Message_Payload_Container(rx_nas.dl_Nas_Transport.payloadContainerType.container, rx_nas.dl_Nas_Transport.payload.payload); if (not match(inner_nas, cr_NG_PDU_SESSION_MODIFICATION_COMMAND(cs_NG_PDU_SessionIdV(pdu_sess_id), pti))) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected decoded NAS Payload Container ", inner_nas)); } nas_ul_msg := cs_NG_PDU_SESSION_MODIFICATION_COMPLETE(cs_NG_PDU_SessionIdV(pdu_sess_id), pti); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); nas_ul_msg := cs_NG_UL_NAS_TRANSPORT(cs_PayloadContainerType(tsc_PayloadContainerESMMsg), cs_PayloadContainerLV(nas_pdu), p_PDU_SessionId := cs_NG_PDU_SessionIdTV(pdu_sess_id)); NGAP.send(nas_ul_msg); } function f_register() runs on ConnHdlr { var template (value) NGAP_PDU tx_pdu; var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NAS_PDU nas_pdu; var NG_NAS_DL_Message_Type rx_nas; nas_ul_msg := cs_NG_REGISTRATION_REQUEST(cs_RegistrationType(tsc_NG_RegistrationInitial, '1'B), g_pars.kset_id.nasKeySetId, g_pars.kset_id.tsc, cs_NG_MobileIdentitySUCI('0000'B /* Type IMSI */, f_SUCI_IMSI()), p_UESecurityCap := f_UE_SecurityCapability()); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); tx_pdu := m_ngap_initMsg(m_n2_initialUeMessage(g_pars.ue_pars.ran_id, nas_pdu, /* Registration request */ f_ULI(), mo_Signalling)); NGAP.send(tx_pdu); /* Expect updated KSI from network following 5G Core standards *(open5gs.git 70310979c58fe186e9eaa06bec9d9a31f24ff7a1): */ if (g_pars.kset_id.nasKeySetId != tsc_NasKsi_NoKey) { if (g_pars.kset_id.nasKeySetId == '110'B) { g_pars.kset_id.nasKeySetId := '000'B; } else { g_pars.kset_id.nasKeySetId := int2bit(bit2int(g_pars.kset_id.nasKeySetId) + 1, 3); } } as_ngap_handle_auth(); as_ngap_handle_sec_mode(); /* After receiving DL NAS Transport above, the UE-AMF-Id became known in NGAP_Emulation: */ g_pars.ue_pars.amf_id := f_ngap_obtain_amf_id(); NGAP.receive(cr_NG_REGISTRATION_ACCEPT) -> value rx_nas; g_pars.ue_pars.guti := rx_nas.registration_Accept.guti.otherDigits; NGAP.send(cs_NG_REGISTRATION_COMPLETE); as_ngap_handle_configuration_update(); } function f_periodic_register_update() runs on ConnHdlr { var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NG_NAS_DL_Message_Type rx_nas; nas_ul_msg := cs_NG_REGISTRATION_REQUEST(cs_RegistrationType(tsc_NG_RegistrationPeriodic, '1'B), g_pars.kset_id.nasKeySetId, g_pars.kset_id.tsc, cs_NG_MobileIdentity_Guti(omit, g_pars.ue_pars.guti), p_UESecurityCap := f_UE_SecurityCapability()); NGAP.send(nas_ul_msg); /* Expect updated KSI from network following 5G Core standards * (open5gs.git 70310979c58fe186e9eaa06bec9d9a31f24ff7a1): */ if (g_pars.kset_id.nasKeySetId != tsc_NasKsi_NoKey) { if (g_pars.kset_id.nasKeySetId == '110'B) { g_pars.kset_id.nasKeySetId := '000'B; } else { g_pars.kset_id.nasKeySetId := int2bit(bit2int(g_pars.kset_id.nasKeySetId) + 1, 3); } } NGAP.receive(cr_NG_REGISTRATION_ACCEPT) -> value rx_nas; g_pars.ue_pars.guti := rx_nas.registration_Accept.guti.otherDigits; NGAP.send(cs_NG_REGISTRATION_COMPLETE); as_ngap_handle_configuration_update(); } /* 3GPP TS 24.501 5.5.2.2 UE-initiated de-registration procedure, * 3GPP TS 23.502 4.2.2.3.2 UE-initiated Deregistration */ function f_deregister() runs on ConnHdlr { var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NG_NAS_DL_Message_Type rx_nas; nas_ul_msg := cs_NG_DEREGISTRATION_REQUEST_MO(crs_DeregisterType ('0'B,/* p_SwitchOff */ '0'B,/* p_ReReg */ '01'B/* p_Access */), g_pars.kset_id, cs_NG_MobileIdentity_Guti(omit, g_pars.ue_pars.guti)); NGAP.send(nas_ul_msg); NGAP.receive(cr_NG_DEREGISTRATION_ACCEPT_MO); as_ngap_handle_UeContextReleaseCmd(); } /* 3GPP TS 38.413 8.3.3 UE Context Release (AMF initiated) */ private altstep as_ngap_handle_UeContextReleaseCmd(template (present) Cause exp_cause := ?) runs on ConnHdlr { var NGAP_PDU rx_ngap; var template (present) NGAP_PDU exp_ngap := mw_ngap_initMsg(mw_n2_UEContextReleaseCommand( mw_uE_NGAP_IDs_uE_NGAP_ID_pair( mw_uE_NGAP_ID_pair(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id)), p_cause := exp_cause)); [] NGAP.receive(exp_ngap) -> value rx_ngap { NGAP.send(m_ngap_succMsg(f_ts_n2_UEContextReleaseComplete(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, f_ULI()))); } } /* 3GPP TS 38.413 8.3.2 UE Context Release Request (NG-RAN node initiated), * 3GPP TS 23.502 4.2.6 AN Release */ function f_ue_context_release(template (value) Cause cause := m_cause_radioNetwork(unspecified)) runs on ConnHdlr { var template (value) NGAP_PDU tx_pdu; if (ispresent(g_pars.ue_pars.sess_pars.cn_gtpu_teid)) { var template (value) PDUSessionResourceListCxtRelReq li_req; var template (value) PDUSessionResourceItemCxtRelReq it_req; it_req := m_pDUSessionResourceItemCxtRelReq(g_pars.ue_pars.sess_pars.id); li_req := { it_req }; tx_pdu := m_ngap_initMsg(m_n2_UEContextReleaseRequest_withPDUSessionList( g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, li_req, cause)); } else { tx_pdu := m_ngap_initMsg(m_n2_UEContextReleaseRequest( g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, cause)); } NGAP.send(tx_pdu); as_ngap_handle_UeContextReleaseCmd(exp_cause := cause); } /* 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 { var PDUSessionResourceReleasedItemRelRes resp; var NAS_CauseValue_Type nas_cause; nas_cause := oct2bit(cmd.pDUSessionResourceReleaseCommandTransfer); g_pars.ue_pars.sess_pars.cn_gtpu_ip := omit; g_pars.ue_pars.sess_pars.cn_gtpu_teid := omit; /* Prepare Response for it: */ resp := valueof(m_pDUSessionResourceReleasedItemRelRes(cmd.pDUSessionID, bit2oct(nas_cause))); return resp; } private function f_pdu_handle_session_resource_released_list(PDUSessionResourceToReleaseListRelCmd li_cmd) runs on ConnHdlr return PDUSessionResourceReleasedListRelRes { var PDUSessionResourceReleasedListRelRes li_resp; for (var integer i := 0; i < lengthof(li_cmd); i := i + 1) { var PDUSessionResourceReleasedItemRelRes it_resp; it_resp := f_pdu_handle_session_resource_released_item(li_cmd[i]); if (i == 0) { /* min 1 item in list doesn't allow previously allocating an empty list */ li_resp := { it_resp }; } else { li_resp := li_resp & { it_resp }; } } return li_resp; } private altstep as_ngap_handle_PDUSessionReleaseCommand() runs on ConnHdlr { var NGAP_PDU rx_ngap; var template (present) NGAP_PDU exp_ngap := mw_ngap_initMsg(f_tr_n2_PDUSessionResourceReleaseCommand(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, ?, ?)); [] NGAP.receive(exp_ngap) -> value rx_ngap { var PDUSessionResourceReleaseCommand rel_cmd := rx_ngap.initiatingMessage.value_.PDUSessionResourceReleaseCommand; var PDUSessionResourceReleasedListRelRes resources; for (var integer i := 0; i < lengthof(rel_cmd.protocolIEs); i := i + 1) { if (rel_cmd.protocolIEs[i].id != id_PDUSessionResourceToReleaseListRelCmd) { continue; } var PDUSessionResourceToReleaseListRelCmd li := rel_cmd.protocolIEs[i].value_.pDUSessionResourceToReleaseListRelCmd; resources := f_pdu_handle_session_resource_released_list(li); } NGAP.send(m_ngap_succMsg(m_n2_PDUSessionResourceReleaseResponse(g_pars.ue_pars.amf_id, g_pars.ue_pars.ran_id, resources))); } } function f_pdu_sess_release(boolean configure_userplane := true) runs on ConnHdlr { var template (value) NG_NAS_UL_Message_Type nas_ul_msg; var NAS_PDU nas_pdu; var NG_NAS_DL_Message_Type rx_nas, inner_nas; var ProcedureTransactionIdentifier pti := f_next_pti(); var NAS_CauseValue_Type cause_val := c_5GSM_Cause_RegularDeactivation; var OCT1 pdu_sess_id := int2oct(g_pars.ue_pars.sess_pars.id, 1); nas_ul_msg := cs_NG_PDU_SESSION_RELEASE_REQUEST(cs_NG_PDU_SessionIdV(pdu_sess_id), pti, p_Cause := cs_GMM_GSM_Cause('59'O, cause_val)); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); nas_ul_msg := cs_NG_UL_NAS_TRANSPORT(cs_PayloadContainerType(tsc_PayloadContainerESMMsg), cs_PayloadContainerLV(nas_pdu), p_PDU_SessionId := cs_NG_PDU_SessionIdTV(pdu_sess_id), p_RequestType := cs_NG_Request_TypeTV('001'B), p_DNN := cs_DNN_TLV(f_enc_dns_hostname(g_pars.ue_pars.apn))); NGAP.send(nas_ul_msg); as_ngap_handle_PDUSessionReleaseCommand(); /* 5G-NAS PDU Session Release Command: This DL NAS arrives contained in NGAP PDUSessionReleaseCommand handled above: */ NGAP.receive(cr_NG_DL_NAS_TRANSPORT) -> value rx_nas; inner_nas := f_dec_NG_NAS_DL_Message_Payload_Container(rx_nas.dl_Nas_Transport.payloadContainerType.container, rx_nas.dl_Nas_Transport.payload.payload); if (not match(inner_nas, cr_NG_PDU_SESSION_RELEASE_COMMAND(cs_NG_PDU_SessionIdV(pdu_sess_id), pti, cs_GMM_GSM_Cause(omit, cause_val)))) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected decoded NAS Payload Container ", inner_nas)); } g_pars.ue_pars.sess_pars.qos_rules := omit; g_pars.ue_pars.sess_pars.ue_ip := omit; nas_ul_msg := cs_NG_PDU_SESSION_RELEASE_COMPLETE(cs_NG_PDU_SessionIdV(pdu_sess_id), pti, p_Cause := cs_GMM_GSM_Cause('59'O, cause_val)); nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg)); nas_ul_msg := cs_NG_UL_NAS_TRANSPORT(cs_PayloadContainerType(tsc_PayloadContainerESMMsg), cs_PayloadContainerLV(nas_pdu), p_PDU_SessionId := cs_NG_PDU_SessionIdTV(pdu_sess_id)); NGAP.send(nas_ul_msg); if (configure_userplane) { f_pdu_sess_delete_tun(); } } function f_pdu_sess_create_tun() runs on ConnHdlr { var template (value) UECUPS_GtpExtHdr gtp_ext_hdr; var template (value) UECUPS_CreateTun uecups_create; var template (value) UECUPS_GtpExtHdr_PduSessContainer pdu_sess_cont; pdu_sess_cont := ts_UECUPS_GtpExtHdr_PduSessContainer(UECUPS_GtpExtHdr_PduSessContainer_Type_ul_pdu_sess_info, oct2int(g_pars.ue_pars.sess_pars.qos_rules[0].identifier)); gtp_ext_hdr := ts_UECUPS_GtpExtHdr(pdu_session_container := pdu_sess_cont); uecups_create := ts_UECUPS_CreateTun( tx_teid := oct2int(g_pars.ue_pars.sess_pars.cn_gtpu_teid), rx_teid := oct2int(g_pars.ue_pars.sess_pars.ran_gtpu_teid), user_addr_type := IPV4, user_addr := f_inet_addr(g_pars.ue_pars.sess_pars.ue_ip), local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.sess_pars.ran_gtpu_ip))), remote_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.sess_pars.cn_gtpu_ip))), tun_dev_name := g_pars.ue_pars.sess_pars.run_prog_pars.tun_dev_name, tun_netns_name := g_pars.ue_pars.sess_pars.run_prog_pars.tun_netns_name, gtp_ext_hdr := gtp_ext_hdr ); f_gtp1u_create_tunnel(uecups_create); } /* destroy tunnel in daemon */ function f_pdu_sess_delete_tun() runs on ConnHdlr { var template (value) UECUPS_DestroyTun uecups_destroy; uecups_destroy := ts_UECUPS_DestroyTun( local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.sess_pars.ran_gtpu_ip))), rx_teid := oct2int(g_pars.ue_pars.sess_pars.ran_gtpu_teid) ); f_gtp1u_destroy_tunnel(uecups_destroy); } private function f_run_prog_init(charstring command) runs on ConnHdlr return UECUPS_StartProgram { var template (value) UECUPS_StartProgram sprog := ts_UECUPS_StartProgram( command := command, environment := {}, run_as_user := g_pars.ue_pars.sess_pars.run_prog_pars.run_as_user, tun_netns_name := g_pars.ue_pars.sess_pars.run_prog_pars.tun_netns_name ); return valueof(sprog); } private function f_run_prog_unique_log_path() runs on ConnHdlr return charstring { var charstring id := testcasename() & "-" & hex2str(g_pars.ue_pars.imsi) & "-" & int2str(g_start_prog_count); var charstring prefix := g_pars.ue_pars.sess_pars.run_prog_pars.log_path_prefix & "/" & id; g_start_prog_count := g_start_prog_count + 1; return prefix; } /* execute ping command and wait for result */ function f_ping4(charstring host, integer interval := 1, integer count := 10) runs on ConnHdlr { /* command will be filled in by f_gtp1u_ping4() below: */ var UECUPS_StartProgram sprog := f_run_prog_init(""); f_gtp1u_ping4(sprog, host, interval, count, g_pars.ue_pars.sess_pars.ue_ip, redirect_output := true, redirect_output_path_prefix := f_run_prog_unique_log_path()); } }