From 8125f8a2eb52d3bd2f9ad3c9ba6a8b2e29b8fee9 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 9 Dec 2025 16:23:18 +0100 Subject: [PATCH] PDU Session Modify: extend PDU Session Modify Transfer (NGAP/RRC) with QoS add/modify/release This commit extends the PDU Session Resource Modify Request handling (within PDU Session Modify) to fully support QoS Flow add/modify and release operations (3GPP TS 38.413 section 9.3.4.3) via the Transfer IE. This is propagated to RRC which performs PDU Session update, which occurs upon PDU Session Modify. Refactoring was necessary since type `pdusession_transfer_t` and `pdusession_resource_item_t` both for setup, were inaccurately used for the modify procedure. The implementation adds proper type definitions, decoding logic, and error handling for QoS flow management during PDU session modification transfer, namely: - Adds QoS add/modify and release lists to the NGAP Modify Request Transfer type - Introduces QoS Flow With Cause IE - Decode new IEs and propagate QoS changes to RRC session state NGAP PDU Session Modify Request Transfer: - Add qos_flow_to_release_t structure to represent QoS Flow to Release Items IE with QFI and release cause (per 3GPP TS 38.413 section 9.3.1.13) - Introduce pdusession_mod_req_transfer_t structure to properly represent PDU Session Resource Modify Request Transfer IEs: * QoS Flow Add or Modify Request List (mandatory) * QoS Flow to Release List (optional) - Add pdusession_resource_mod_item_t structure for PDU Session Resource Modify Request Items, replacing pdusession_resource_item_t, which is for setup - Update ngap_pdusession_modify_req_t to use the new type-specific structure - Refactor decodePDUSessionResourceModify() to return pdusession_mod_req_transfer_t instead of pdusession_transfer_t - Implement proper decoding of QosFlowAddOrModifyRequestList IE - Add decoding support for QosFlowToReleaseList (QosFlowListWithCause) IE: * Extract QFI and cause for each QoS flow to be released - Improve error handling throughout ngap_msg_includes.h: - Add includes for NGAP_QosFlowListWithCause.h and NGAP_QosFlowWithCauseItem.h to support QoS flow release decoding NGAP/RRC: - Update nr_rrc_update_pdusession() function signature to accept pdusession_resource_mod_item_t instead of pdusession_resource_item_t - Remove references to unnecessary pdu_session_type and n3_incoming fields that are not part of the Modify Request Transfer structure - Update QoS flow update logic to use nb_qos_to_add_modify and qos_to_add_modify fields from the new transfer structure This implementation aligns NGAP with 3GPP TS 38.413 specifications and provides a foundation for handling QoS flow modifications and releases during PDU session resource modification procedures. Signed-off-by: Guido Casati --- openair2/COMMON/ngap_messages_types.h | 40 ++++++++++++---- openair2/RRC/NR/rrc_gNB_NGAP.c | 9 ++-- openair3/NGAP/ngap_gNB_handlers.c | 69 ++++++++++++++++++++++----- openair3/NGAP/ngap_msg_includes.h | 3 +- 4 files changed, 96 insertions(+), 25 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 8bb32c8e99..6a77ac6e45 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -845,18 +845,42 @@ typedef struct { uint8_t pdu_session_id[NR_MAX_NB_PDU_SESSIONS]; } ngap_ue_release_complete_t; +/* QoS Flow to Release Item (9.3.1.13 3GPP TS 38.413) */ +typedef struct qos_flow_to_release_s { + uint8_t qfi; + ngap_cause_t cause; +} qos_flow_to_release_t; + +/* PDU Session Resource Modify Request Transfer (9.3.4.3 3GPP TS 38.413) */ +typedef struct { + // QoS Flow Add or Modify Request List (Mandatory) + uint8_t nb_qos_to_add_modify; + pdusession_level_qos_parameter_t qos_to_add_modify[MAX_QOS_FLOWS]; + // QoS Flow to Release List (Optional) + uint8_t nb_qos_to_release; + qos_flow_to_release_t qos_to_release[MAX_QOS_FLOWS]; +} pdusession_mod_req_transfer_t; + +/* PDU Session Resource Setup/Modify Request Item */ +typedef struct { + // PDU Session ID (Mandatory) + int pdusession_id; + // NAS PDU (Optional) + byte_array_t nas_pdu; + // S-NSSAI (Optional) + nssai_t nssai; + // PDU Session Resource Modify Request Transfer (Mandatory) + pdusession_mod_req_transfer_t pdusessionTransfer; +} pdusession_resource_mod_item_t; + typedef struct ngap_pdusession_modify_req_s { - /* AMF UE id */ + /* AMF UE NGAP ID (Mandatory) */ uint64_t amf_ue_ngap_id; - - /* gNB ue ngap id as initialized by NGAP layer */ + /* RAN UE NGAP ID (Mandatory) */ uint32_t gNB_ue_ngap_id; - - /* Number of pdusession to be modify in the list */ + /* PDU Session Resource Modify Request List (Mandatory) */ uint16_t nb_pdusessions_tomodify; - - // PDU Session Resource Modify Request List - pdusession_resource_item_t pdusession[NR_MAX_NB_PDU_SESSIONS]; + pdusession_resource_mod_item_t pdusession[NR_MAX_NB_PDU_SESSIONS]; } ngap_pdusession_modify_req_t; /* 9.2.1.6 of 3GPP TS 38.413 */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 29cc49ece2..1cc4c4505c 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1005,16 +1005,15 @@ static void nr_rrc_update_qos(seq_arr_t *list, const int nb_qos, const pdusessio } /** @brief Update stored pdusession_t in list from NGAP PDU Session Resource item */ -static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src) +static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resource_mod_item_t *src) { dst->pdusession_id = src->pdusession_id; dst->nas_pdu = src->nas_pdu; - dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type; - dst->n3_incoming = src->pdusessionTransfer.n3_incoming; dst->nssai = src->nssai; DevAssert(dst->qos.data); // QoS list has to be initialized at this stage // Update QoS flow - nr_rrc_update_qos(&dst->qos, src->pdusessionTransfer.nb_qos, src->pdusessionTransfer.qos); + const pdusession_mod_req_transfer_t *transfer = &src->pdusessionTransfer; + nr_rrc_update_qos(&dst->qos, transfer->nb_qos_to_add_modify, transfer->qos_to_add_modify); } //------------------------------------------------------------------------------ @@ -1065,7 +1064,7 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(const ngap_pdusession_modify_req_ bool all_failed = true; DevAssert(req->nb_pdusessions_tomodify <= NR_MAX_NB_PDU_SESSIONS); for (int i = 0; i < req->nb_pdusessions_tomodify; i++) { - const pdusession_resource_item_t *sessMod = &req->pdusession[i]; + const pdusession_resource_mod_item_t *sessMod = &req->pdusession[i]; rrc_pdu_session_param_t *session = find_pduSession(&UE->pduSessions, sessMod->pdusession_id); if (!session) { /* 8.2.3.4 3GPP TS 38.413: If the NG-RAN node receives unrecognized PDU Session ID IEs, diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index bb56d31018..22ded50b74 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -1148,7 +1148,7 @@ static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_N return 0; } -static bool decodePDUSessionResourceModify(pdusession_transfer_t *out, const OCTET_STRING_t in) +static bool decodePDUSessionResourceModify(pdusession_mod_req_transfer_t *out, const OCTET_STRING_t in) { void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, in); if (!decoded) { @@ -1156,23 +1156,69 @@ static bool decodePDUSessionResourceModify(pdusession_transfer_t *out, const OCT return false; } + // Reset counters + out->nb_qos_to_add_modify = 0; + out->nb_qos_to_release = 0; + NGAP_PDUSessionResourceModifyRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceModifyRequestTransfer_t *)decoded; for (int j = 0; j < pdusessionTransfer->protocolIEs.list.count; j++) { NGAP_PDUSessionResourceModifyRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[j]; + if (!pdusessionTransfer_ies) { + LOG_E(NGAP, "NULL protocol IE at index %d in PDUSessionResourceModifyRequestTransfer\n", j); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } switch (pdusessionTransfer_ies->id) { /* optional QosFlowAddOrModifyRequestList */ - case NGAP_ProtocolIE_ID_id_QosFlowAddOrModifyRequestList: - out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count; - for (int i = 0; i < out->nb_qos; i++) { - NGAP_QosFlowAddOrModifyRequestItem_t *item = - pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.array[i]; - out->qos[i] = fill_qos(item->qosFlowIdentifier, item->qosFlowLevelQosParameters); + case NGAP_ProtocolIE_ID_id_QosFlowAddOrModifyRequestList: { + NGAP_QosFlowAddOrModifyRequestList_t *addmod = &pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList; + out->nb_qos_to_add_modify = addmod->list.count; + if (out->nb_qos_to_add_modify > MAX_QOS_FLOWS) { + LOG_E(NGAP, "QosFlowAddOrModifyRequestList count %d exceeds MAX_QOS_FLOWS %d\n", + out->nb_qos_to_add_modify, MAX_QOS_FLOWS); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } + for (int i = 0; i < out->nb_qos_to_add_modify; i++) { + NGAP_QosFlowAddOrModifyRequestItem_t *item = addmod->list.array[i]; + if (!item) { + LOG_E(NGAP, "NULL QosFlowAddOrModifyRequestItem at index %d\n", i); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } + if (!item->qosFlowLevelQosParameters) { + LOG_E(NGAP, "NULL qosFlowLevelQosParameters for QFI %ld\n", item->qosFlowIdentifier); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } + out->qos_to_add_modify[i] = fill_qos(item->qosFlowIdentifier, item->qosFlowLevelQosParameters); } break; - + } + case NGAP_ProtocolIE_ID_id_QosFlowToReleaseList: { + NGAP_QosFlowListWithCause_t *list = &pdusessionTransfer_ies->value.choice.QosFlowListWithCause; + out->nb_qos_to_release = list->list.count; + if (out->nb_qos_to_release > MAX_QOS_FLOWS) { + LOG_E(NGAP, "QosFlowToReleaseList count %d exceeds MAX_QOS_FLOWS %d\n", + out->nb_qos_to_release, MAX_QOS_FLOWS); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } + for (int i = 0; i < out->nb_qos_to_release; i++) { + NGAP_QosFlowWithCauseItem_t *item = list->list.array[i]; + if (!item) { + LOG_E(NGAP, "NULL QosFlowWithCauseItem at index %d\n", i); + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); + return false; + } + out->qos_to_release[i].qfi = item->qosFlowIdentifier; + out->qos_to_release[i].cause = decode_ngap_cause(&item->cause); + } + break; + } default: - LOG_E(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id); - return false; + LOG_W(NGAP, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id); + continue; } } ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, pdusessionTransfer); @@ -1262,7 +1308,8 @@ static int ngap_gNB_handle_pdusession_modify_request(sctp_assoc_t assoc_id, uint if (item_p->nAS_PDU != NULL && item_p->nAS_PDU->size > 0) { msg->pdusession[i].nas_pdu = create_byte_array(item_p->nAS_PDU->size, item_p->nAS_PDU->buf); if (!decodePDUSessionResourceModify(&msg->pdusession[i].pdusessionTransfer, item_p->pDUSessionResourceModifyRequestTransfer)) { - NGAP_ERROR("Failed to decode pDUSessionResourceModifyRequestTransfer\n"); + NGAP_ERROR("Failed to decode pDUSessionResourceModifyRequestTransfer for PDU session %lu\n", item_p->pDUSessionID); + itti_free(TASK_NGAP, message_p); return -1; } } else { diff --git a/openair3/NGAP/ngap_msg_includes.h b/openair3/NGAP/ngap_msg_includes.h index 49f499d96a..1a42183a62 100644 --- a/openair3/NGAP/ngap_msg_includes.h +++ b/openair3/NGAP/ngap_msg_includes.h @@ -95,5 +95,6 @@ #include "NGAP_DRBStatusDL.h" #include "NGAP_DRBStatusDL18.h" #include "NGAP_DRBStatusDL12.h" - +#include "NGAP_QosFlowListWithCause.h" +#include "NGAP_QosFlowWithCauseItem.h" #endif // NGAP_MSG_INCLUDES_H