NGAP Path Switch Request Acknowledge: add decode and handler

- Add handler at NGAP for Path Switch Request Acknowledge from AMF
- Add Path Switch Request Acknowledge message type definitions
- Implement decoder for Path Switch Request Acknowledge
  (Reference: 3GPP TS 38.413 §9.2.3.9)
    - AMF UE NGAP ID (M)
    - RAN UE NGAP ID (M)
    - Security Context (M)
    - PDU Session Resource Switched List (M)
    - Allowed NSSAI (M)
- Add required ASN.1 header includes for Path Switch Request Acknowledge 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 06:35:47 +00:00
parent db7dbda8e2
commit 829d6fe7cf
7 changed files with 193 additions and 1 deletions

View File

@@ -45,6 +45,8 @@
#define NR_MAX_NB_PDU_SESSIONS (256)
#define NR_MAX_NB_ALLOWED_SNSSAI (8) /* Maximum number of allowed S-NSSAI in TS 38.413 */
#define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE
* defined for NGAP in TS 38.413 - maxnoofDRBs */
#define MAX_PDUS_PER_UE (8) /* Maximum number of PDU Sessions per UE */

View File

@@ -54,6 +54,7 @@ MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_t
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)
MESSAGE_DEF(NGAP_PATH_SWITCH_REQ_ACK, MESSAGE_PRIORITY_MED, ngap_path_switch_req_ack_t, ngap_path_switch_req_ack)
/* NGAP -> RRC messages */
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )

View File

@@ -48,6 +48,7 @@
#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_PATH_SWITCH_REQ_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req_ack
#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
@@ -686,6 +687,53 @@ typedef struct ngap_path_switch_req_s {
pdusession_setup_t pdusessions_tobeswitched[NR_MAX_NB_PDU_SESSIONS];
} ngap_path_switch_req_t;
typedef enum ngap_security_ind_s {
NGAP_SECURITY_REQUIRED = 0,
NGAP_SECURITY_PREFERRED = 1,
NGAP_SECURITY_NOT_NEEDED = 2,
} ngap_security_ind_t;
/* 9.3.1.27 3GPP TS 38.413 */
typedef struct security_ind_s {
ngap_security_ind_t integrity_protection_ind;
ngap_security_ind_t confidentiality_protection_ind;
} security_ind_t;
/* 9.3.4.9 3GPP TS 38.413 */
typedef struct path_switch_request_ack_transfer_s {
// UL NG-U UP TNL Information (O)
gtpu_tunnel_t *n3_incoming;
// Security Indication (O)
security_ind_t *security_ind;
} path_switch_request_ack_transfer_t;
/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413
* PDU Session Resource Switched Item */
typedef struct path_switch_request_ack_pdusession_s {
// PDU Session ID (M)
int pdusession_id;
// Path Switch Request Acknowledge Transfer (M)
path_switch_request_ack_transfer_t pathSwitchReqAckTransfer;
} path_switch_request_ack_pdusession_t;
/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413 */
typedef struct ngap_path_switch_req_ack_s {
// AMF UE NGAP ID (M)
uint64_t amf_ue_ngap_id;
// RAN UE NGAP ID (M)
uint32_t gNB_ue_ngap_id;
// Security Context - Next-Hop Chaining Count (M)
uint8_t nh_ncc;
// Security Context - Next-Hop NH (M)
uint8_t next_security_key[SECURITY_KEY_LENGTH];
// List of PDU Session Resource Switched (M)
uint16_t nb_of_pdusessions;
path_switch_request_ack_pdusession_t pdusessions_switched[NR_MAX_NB_PDU_SESSIONS];
// Allowed NSSAI (M)
uint8_t nb_allowed_nssais;
nssai_t allowed_nssai[NR_MAX_NB_ALLOWED_SNSSAI];
} ngap_path_switch_req_ack_t;
typedef struct ngap_ue_cap_info_ind_s {
uint32_t gNB_ue_ngap_id;
byte_array_t ue_radio_cap;

View File

@@ -1092,6 +1092,54 @@ static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t s
return 0;
}
/** @brief Handler for NGAP Path Switch Request Acknowledge
* AMF -> NG-RAN Node */
static int ngap_gNB_handle_ng_path_switch_request_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
NGAP_INFO("Received NG Path Switch Request Acknowledge\n");
ngap_gNB_amf_data_t *amf_desc_p = NULL;
DevAssert(pdu != NULL);
if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) {
NGAP_ERROR("[SCTP %u] Received Path Switch Request "
"Acknowledge for non existing AMF context\n", assoc_id);
return -1;
}
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PATH_SWITCH_REQ_ACK);
ngap_path_switch_req_ack_t *msg = &NGAP_PATH_SWITCH_REQ_ACK(message_p);
memset(msg, 0, sizeof(*msg));
if (decode_ng_path_switch_request_acknowledge(msg, pdu) < 0) {
NGAP_ERROR("Failed to decode NG Path Switch Request Acknowledge\n");
free_ng_path_switch_req_ack(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (!ue_desc_p) {
NGAP_ERROR("[SCTP %u] Received Path Switch Request Acknowledge for non "
"existing UE context (gNB_ue_ngap_id %d)\n",
assoc_id,
msg->gNB_ue_ngap_id);
free_ng_path_switch_req_ack(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
ue_desc_p->rx_stream = stream;
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_ERROR("UE context amf_ue_ngap_id is different from that of the message (%ld != %ld)",
ue_desc_p->amf_ue_ngap_id,
msg->amf_ue_ngap_id);
free_ng_path_switch_req_ack(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
return 0;
}
/**
* @brief Handle NGAP Paging message from AMF
* @param assoc_id SCTP association ID
@@ -1542,7 +1590,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = {
{0, 0, 0}, /* OverloadStart */
{0, 0, 0}, /* OverloadStop */
{ngap_gNB_handle_paging, 0, 0}, /* Paging */
{0, 0, 0}, /* PathSwitchRequest */
{0, ngap_gNB_handle_ng_path_switch_request_ack, 0}, /* PathSwitchRequest */
{ngap_gNB_handle_pdusession_modify_request, 0, 0}, /* PDUSessionResourceModify */
{0, 0, 0}, /* PDUSessionResourceModifyIndication */
{ngap_gNB_handle_pdusession_release_command, 0, 0}, /* PDUSessionResourceRelease */

View File

@@ -824,3 +824,92 @@ NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg
}
return pdu;
}
void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg)
{
if (msg == NULL)
return;
for (int i = 0; i < msg->nb_of_pdusessions; i++) {
path_switch_request_ack_transfer_t *transfer = &msg->pdusessions_switched[i].pathSwitchReqAckTransfer;
free(transfer->n3_incoming);
transfer->n3_incoming = NULL;
free(transfer->security_ind);
transfer->security_ind = NULL;
}
}
/** @brief Decode NGAP Path Switch Request Acknowledge (9.2.3.9 3GPP TS 38.413) */
int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu)
{
DevAssert(pdu != NULL);
NGAP_PathSwitchRequestAcknowledgeIEs_t *ie;
NGAP_PathSwitchRequestAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.PathSwitchRequestAcknowledge;
// AMF UE NGAP ID (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
// Security Context (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true);
msg->nh_ncc = ie->value.choice.SecurityContext.nextHopChainingCount;
memcpy(msg->next_security_key, ie->value.choice.SecurityContext.nextHopNH.buf, ie->value.choice.SecurityContext.nextHopNH.size);
// PDU Session Resource Switched List (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceSwitchedList, true);
msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSwitchedList.list.count;
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
NGAP_PDUSessionResourceSwitchedItem_t *item = ie->value.choice.PDUSessionResourceSwitchedList.list.array[i];
path_switch_request_ack_pdusession_t *switched = &msg->pdusessions_switched[i];
// PDU Session ID (M)
switched->pdusession_id = item->pDUSessionID;
// Path Switch Request Acknowledge Transfer (M)
NGAP_PathSwitchRequestAcknowledgeTransfer_t *psrAckTransfer = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer,
(void **)&psrAckTransfer,
item->pathSwitchRequestAcknowledgeTransfer.buf,
item->pathSwitchRequestAcknowledgeTransfer.size);
if (dec_rval.code != RC_OK) {
NGAP_ERROR("Failed to decode Path Switch Request Acknowledge Transfer\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer);
return -1;
}
// UL NG-U UP TNL Information (O)
if (psrAckTransfer->uL_NGU_UP_TNLInformation != NULL) {
NGAP_UPTransportLayerInformation_t *up_tnl = psrAckTransfer->uL_NGU_UP_TNLInformation;
if (up_tnl->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel && up_tnl->choice.gTPTunnel != NULL) {
switched->pathSwitchReqAckTransfer.n3_incoming = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.n3_incoming));
OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), switched->pathSwitchReqAckTransfer.n3_incoming->teid);
bitstring_to_tnl(&switched->pathSwitchReqAckTransfer.n3_incoming->addr, up_tnl->choice.gTPTunnel->transportLayerAddress);
}
}
// Security Indication (O)
if (psrAckTransfer->securityIndication != NULL) {
switched->pathSwitchReqAckTransfer.security_ind = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.security_ind));
switched->pathSwitchReqAckTransfer.security_ind->integrity_protection_ind = psrAckTransfer->securityIndication->integrityProtectionIndication;
switched->pathSwitchReqAckTransfer.security_ind->confidentiality_protection_ind = psrAckTransfer->securityIndication->confidentialityProtectionIndication;
}
ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer);
}
// Allowed NSSAI (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true);
NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count);
msg->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count;
for (int i = 0; i < msg->nb_allowed_nssais; ++i) {
msg->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI);
}
return 0;
}

View File

@@ -21,5 +21,7 @@ NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer
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);
int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu);
void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg);
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */

View File

@@ -100,4 +100,6 @@
#include "NGAP_PDUSessionResourceToBeSwitchedDLItem.h"
#include "NGAP_QosFlowAcceptedItem.h"
#include "NGAP_PathSwitchRequestTransfer.h"
#include "NGAP_PDUSessionResourceSwitchedItem.h"
#include "NGAP_PathSwitchRequestAcknowledgeTransfer.h"
#endif // NGAP_MSG_INCLUDES_H