NGAP Path Switch Request: add encode and ITTI support

- Add a case in TASK_NGAP to support ITTI message from RRC to NGAP
- Add Path Switch Request message type definitions
- Implement encoder for Path Switch Request (3GPP TS 38.413v16.2.0 §9.2.3.8)
    - RAN UE NGAP ID (M)
    - Source AMF UE NGAP ID (M)
    - User Location Information (M)
    - UE Security Capabilities (M)
    - PDU Session Resource to be Switched in Downlink List (M)
- Add required ASN.1 header includes for Path Switch Request IEs

Co-authored-by: Rakesh BB <rakesh.bb@fsid-iisc.in>
Signed-off-by: Venkatareddy Akumalla <venkatareddy.akumalla@openairinterface.org>
This commit is contained in:
Venkatareddy Akumalla
2026-06-16 05:41:01 +00:00
parent f3589d97c2
commit db7dbda8e2
8 changed files with 177 additions and 0 deletions

View File

@@ -53,6 +53,7 @@ MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t,
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
MESSAGE_DEF(NGAP_RECONNECT_TIMER, MESSAGE_PRIORITY_MED, IttiMsgText, ngap_gNB_amf_data)
MESSAGE_DEF(NGAP_PATH_SWITCH_REQ, MESSAGE_PRIORITY_MED, ngap_path_switch_req_t, ngap_path_switch_req)
/* NGAP -> RRC messages */
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )

View File

@@ -47,6 +47,7 @@
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack
#define NGAP_PATH_SWITCH_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req
#define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req
#define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command
@@ -669,6 +670,22 @@ typedef struct {
uint64_t amf_ue_ngap_id;
} ngap_handover_cancel_ack_t;
/* Path Switch Request 9.2.3.8 3GPP TS 38.413 */
typedef struct ngap_path_switch_req_s {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// Source AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// User Location Information
user_location_information_t user_info;
// UE Security Capabilities
ngap_security_capabilities_t security_capabilities;
// Number of pdusession to be switched in the downlink list
uint16_t nb_of_pdusessions;
// List of PDU Session Resource to be Switched in Downlink
pdusession_setup_t pdusessions_tobeswitched[NR_MAX_NB_PDU_SESSIONS];
} ngap_path_switch_req_t;
typedef struct ngap_ue_cap_info_ind_s {
uint32_t gNB_ue_ngap_id;
byte_array_t ue_radio_cap;

View File

@@ -133,6 +133,14 @@ nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in)
return nssai;
}
void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in)
{
ENCRALG_TO_BIT_STRING(in->nRencryption_algorithms, &out->nRencryptionAlgorithms);
ENCRALG_TO_BIT_STRING(in->eUTRAencryption_algorithms, &out->eUTRAencryptionAlgorithms);
INTPROTALG_TO_BIT_STRING(in->nRintegrity_algorithms, &out->nRintegrityProtectionAlgorithms);
INTPROTALG_TO_BIT_STRING(in->eUTRAintegrity_algorithms, &out->eUTRAintegrityProtectionAlgorithms);
}
ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in)
{
ngap_security_capabilities_t out = {0};

View File

@@ -41,6 +41,7 @@ pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQo
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf);
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in);
byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession);
void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in);
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b);

View File

@@ -281,6 +281,56 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}
static int ngap_gNB_path_switch_request(instance_t instance, ngap_path_switch_req_t *msg)
{
DevAssert(msg != NULL);
NGAP_DEBUG("Triggered Pathswitch Request\n");
/*Retrieve the NGAP gNB instance associated with Mod_id */
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(ngap_gNB_instance_p != NULL);
NGAP_NGAP_PDU_t *pdu = encode_ng_path_switch_request(msg);
DevAssert(pdu != NULL);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu);
byte_array_t out = { .buf = NULL, .len = 0 };
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
NGAP_ERROR("Failed to encode Path Switch Request message\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ngap_gNB_amf_data_t *amf_ref = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p);
if (!amf_ref) {
NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
// Create and then store the NGAP UE context
ngap_gNB_ue_context_t ue_context_p = {
.amf_ref = amf_ref,
.gNB_ue_ngap_id = msg->gNB_ue_ngap_id,
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
.gNB_instance = ngap_gNB_instance_p,
.ue_state = NGAP_UE_CONNECTED,
};
ngap_store_ue_context(&ue_context_p);
/* UE associated signalling -> use the allocated stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
ue_context_p.amf_ref->assoc_id,
out.buf,
out.len,
ue_context_p.tx_stream);
NGAP_INFO("Sent Path Switch Request to AMF\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return 0;
}
/** @brief UE Mobility Management: callback for Handover Required */
int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg)
{
@@ -613,6 +663,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed)
ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg));
break;
case NGAP_PATH_SWITCH_REQ:
ngap_gNB_path_switch_request(instance, &NGAP_PATH_SWITCH_REQ(received_msg));
break;
case NGAP_HANDOVER_REQUIRED:
if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) {
NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n");

View File

@@ -732,3 +732,95 @@ NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer
return pdu;
}
/** @brief Encode NGAP Path Switch Request (9.2.3.8 3GPP TS 38.413) NG-RAN node -> AMF */
NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg)
{
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1,sizeof(*pdu));
/* Prepare the NGAP message to encode */
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_PathSwitchRequest;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_InitiatingMessage__value_PR_PathSwitchRequest;
NGAP_PathSwitchRequest_t *out = &head->value.choice.PathSwitchRequest;
// RAN UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_PathSwitchRequestIEs__value_PR_RAN_UE_NGAP_ID;
ie1->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
// Source AMF UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_SourceAMF_UE_NGAP_ID;
ie2->criticality = NGAP_Criticality_reject;
ie2->value.present = NGAP_PathSwitchRequestIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie2->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
// User Location Information (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_UserLocationInformation;
ie3->criticality = NGAP_Criticality_ignore;
ie3->value.present = NGAP_PathSwitchRequestIEs__value_PR_UserLocationInformation;
ie3->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR;
asn1cCalloc(ie3->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p);
// NR user location information
const target_ran_node_id_t *target = &msg->user_info.target_ng_ran;
// CGI (M)
MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity);
MCC_MNC_TO_TBCD(target->plmn_identity.mcc,
target->plmn_identity.mnc,
target->plmn_identity.mnc_digit_length,
&userinfo_nr_p->nR_CGI.pLMNIdentity);
// TAI (M)
INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC);
MCC_MNC_TO_PLMNID(target->plmn_identity.mcc,
target->plmn_identity.mnc,
target->plmn_identity.mnc_digit_length,
&userinfo_nr_p->tAI.pLMNIdentity);
}
// UE Security Capabilities (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie4);
ie4->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities;
ie4->criticality = NGAP_Criticality_ignore;
ie4->value.present = NGAP_PathSwitchRequestIEs__value_PR_UESecurityCapabilities;
encode_ngap_security_capabilities(&ie4->value.choice.UESecurityCapabilities, &msg->security_capabilities);
// PDU Session Resource to be Switched in Downlink List (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie5);
ie5->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceToBeSwitchedDLList;
ie5->criticality = NGAP_Criticality_reject;
ie5->value.present = NGAP_PathSwitchRequestIEs__value_PR_PDUSessionResourceToBeSwitchedDLList;
for (const pdusession_setup_t *pdu = msg->pdusessions_tobeswitched; pdu < msg->pdusessions_tobeswitched + msg->nb_of_pdusessions; pdu++) {
asn1cSequenceAdd(ie5->value.choice.PDUSessionResourceToBeSwitchedDLList.list, NGAP_PDUSessionResourceToBeSwitchedDLItem_t, item);
item->pDUSessionID = pdu->pdusession_id;
/* dLQoSFlowPerTNLInformation */
NGAP_PathSwitchRequestTransfer_t transfer = {0};
transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp);
GTP_TEID_TO_ASN1(pdu->n3_outgoing.teid, &tmp->gTP_TEID);
tnl_to_bitstring(&tmp->transportLayerAddress, pdu->n3_outgoing.addr);
for (int j = 0; j < pdu->nb_of_qos_flow; j++) {
asn1cSequenceAdd(transfer.qosFlowAcceptedList.list, NGAP_QosFlowAcceptedItem_t, qosItem);
qosItem->qosFlowIdentifier = pdu->associated_qos_flows[j].qfi;
}
void *buf;
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_PathSwitchRequestTransfer, NULL, &transfer, &buf);
AssertFatal(encoded > 0, "ASN1 message encoding failed !\n");
item->pathSwitchRequestTransfer.buf = buf;
item->pathSwitchRequestTransfer.size = encoded;
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PathSwitchRequestTransfer, &transfer);
}
}
return pdu;
}

View File

@@ -20,5 +20,6 @@ NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg);
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg);
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu);
NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg);
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */

View File

@@ -97,4 +97,7 @@
#include "NGAP_DRBStatusDL12.h"
#include "NGAP_QosFlowListWithCause.h"
#include "NGAP_QosFlowWithCauseItem.h"
#include "NGAP_PDUSessionResourceToBeSwitchedDLItem.h"
#include "NGAP_QosFlowAcceptedItem.h"
#include "NGAP_PathSwitchRequestTransfer.h"
#endif // NGAP_MSG_INCLUDES_H