diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 064857a8e3..5a982df256 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -381,12 +381,9 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, { DevAssert(req_drbs != NULL && cellGroupConfig != NULL); instance_t f1inst = get_f1_gtp_instance(); - cellGroupConfig->rlc_BearerToReleaseList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToReleaseList)); AssertFatal(cellGroupConfig->rlc_BearerToReleaseList != NULL, "out of memory\n"); - /* Note: the actual GTP tunnels are already removed in the F1AP message - * decoding */ for (int i = 0; i < drbs_len; i++) { const f1ap_drb_to_release_t *drb = &req_drbs[i]; @@ -401,7 +398,7 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) { nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid); nr_rlc_release_entity(UE->rnti, lcid); - if (f1inst >= 0) + if (f1inst >= 0) /* Delete F1 tunnel */ newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb->id); asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1); long *plcid = malloc(sizeof(*plcid)); diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 4b95963257..ecdf3dfd72 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -259,17 +259,58 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) get_e1_if()->bearer_setup_response(&resp); } +static void release_gtpu_tunnel(uint32_t ue_id, int pdusession_id) +{ + instance_t n3inst = get_n3_gtp_instance(); + + // Handle N3 tunnel (1 tunnel per PDU session) + if (n3inst >= 0) { + LOG_I(E1AP, "UE %u: Deleting N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + int result = newGtpuDeleteOneTunnel(n3inst, ue_id, pdusession_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + } + } +} + +static void release_f1_drbs(uint32_t ue_id, int pdusession_id) +{ + instance_t f1inst = get_f1_gtp_instance(); + // Handle F1 tunnels (1 tunnel per DRB, fetch PDCP entities and get DRB IDs) + int drb_ids[MAX_DRBS_PER_UE]; + int n_drbs = nr_pdcp_get_drb_ids_for_pdusession(ue_id, pdusession_id, drb_ids); + if (n_drbs > 0) { + LOG_I(E1AP, "UE %u: Deleting %d F1 tunnels for PDU session %d\n", ue_id, n_drbs, pdusession_id); + // Delete each F1 tunnel using the DRB IDs + for (int i = 0; i < n_drbs; i++) { + int drb_id = drb_ids[i]; + if (f1inst >= 0) { + int result = newGtpuDeleteOneTunnel(f1inst, ue_id, drb_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete F1 tunnel for DRB %d\n", ue_id, drb_id); + } + } + // Release PDCP entity + nr_pdcp_release_drb(ue_id, drb_id); + } + } else { + LOG_W(E1AP, "UE %u: No DRBs found for PDU session %d\n", ue_id, pdusession_id); + } +} + /** * @brief Fill Bearer Context Modification Response and send to callback */ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { - AssertFatal(req->numPDUSessionsMod > 0, "need at least one PDU session to modify\n"); + AssertFatal(req->numPDUSessionsMod > 0 || req->numPDUSessionsRem > 0, + "need at least one PDU session to modify or release (mod=%u, rel=%u)\n", + req->numPDUSessionsMod, + req->numPDUSessionsRem); e1ap_bearer_modif_resp_t modif = { .gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id, .gNB_cu_up_ue_id = req->gNB_cu_up_ue_id, - .numPDUSessionsMod = req->numPDUSessionsMod, }; instance_t f1inst = get_f1_gtp_instance(); @@ -284,6 +325,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) req->pduSessionMod[i].numDRB2Modify); modif.pduSessionMod[i].id = req->pduSessionMod[i].sessionId; modif.pduSessionMod[i].numDRBModified = req->pduSessionMod[i].numDRB2Modify; + modif.numPDUSessionsMod++; /* DRBs to modify */ for (int j = 0; j < req->pduSessionMod[i].numDRB2Modify; j++) { const DRB_nGRAN_to_mod_t *to_modif = &req->pduSessionMod[i].DRBnGRanModList[j]; @@ -347,6 +389,15 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) } } + /* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */ + for (int i = 0; i < req->numPDUSessionsRem; i++) { + modif.pduSessionMod[i].id = req->pduSessionRem[i].sessionId; + modif.numPDUSessionsMod++; + release_gtpu_tunnel(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + release_f1_drbs(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + nr_sdap_delete_entity(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + } + get_e1_if()->bearer_modif_response(&modif); } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 94d44fe8ce..3fd4c4bdd4 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -653,6 +653,30 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag, LOG_W(PDCP, "nr_pdcp_add_srbs() with void list\n"); } +/** @brief Get DRB IDs for a specific PDU session */ +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids) +{ + int num_found = 0; + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); + if (ue) { + for (int i = 0; i < MAX_DRBS_PER_UE; i++) { + nr_pdcp_entity_t *drb = ue->drb[i]; + if (drb && drb->pdusession_id == pdusession_id) { + if (num_found < MAX_DRBS_PER_UE) { + drb_ids[num_found] = drb->rb_id; + num_found++; + } else { + LOG_W(PDCP, "Found more DRBs than MAX_DRBS_PER_UE=%d\n", MAX_DRBS_PER_UE); + break; + } + } + } + } + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + return num_found; +} + uint64_t get_pdcp_optmask(void) { return pdcp_optmask; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index 884ea11f60..75435ed695 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -66,6 +66,7 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering); void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_config); void nr_pdcp_release_srb(ue_id_t ue_id, int srb_id); void nr_pdcp_release_drb(ue_id_t ue_id, int drb_id); +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids); void add_srb(int is_gnb, ue_id_t UEid, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 2136e966ab..585b1e48e2 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -519,7 +519,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re cfg->drb_ToAddModList = params->drb_config_list; cfg->securityConfig = params->security_config; cfg->srb3_ToRelease = NULL; - cfg->drb_ToReleaseList = params->drb_release_list; + if (params->n_drb_rel) { + asn1cCalloc(cfg->drb_ToReleaseList, to_release); + for (int i = 0; i < params->n_drb_rel; i++) { + asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); + *DRB_release = params->drb_rel[i]; + } + } } /* measConfig */ diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 48a0ea734b..eb8da91c2a 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -64,7 +64,8 @@ typedef struct { uint8_t transaction_id; NR_SRB_ToAddModList_t *srb_config_list; NR_DRB_ToAddModList_t *drb_config_list; - NR_DRB_ToReleaseList_t *drb_release_list; + int *drb_rel; + int n_drb_rel; NR_SecurityConfig_t *security_config; NR_MeasConfig_t *meas_config; byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE]; diff --git a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp index 1f92c5aae5..025978218e 100644 --- a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp +++ b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp @@ -88,7 +88,6 @@ TEST(nr_asn1, paging) void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, params.meas_config); - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 27e17e03f0..e8820bcc76 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -167,8 +167,6 @@ typedef struct nr_handover_context_s nr_handover_context_t; typedef struct gNB_RRC_UE_s { time_t last_seen; // last time this UE has been accessed - NR_DRB_ToReleaseList_t *DRB_ReleaseList; - NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB]; NR_MeasConfig_t *measConfig; nr_handover_context_t *ho_context; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 777ea9a8d1..2fc7bf5e5e 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -66,12 +66,6 @@ void *rrc_gnb_task(void *args_p); int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id); -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer); - NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid); bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index bc4cca38f1..1e08c9cf25 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -629,10 +629,10 @@ static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); + free(params.drb_rel); for (int i = 0; i < params.num_nas_msg; i++) FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]); } @@ -710,19 +710,37 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup, .transaction_id = xid, .drb_config_list = DRBs, - .drb_release_list = UE->DRB_ReleaseList, .meas_config = UE->measConfig, .srb_config_list = SRBs}; - UE->DRB_ReleaseList = NULL; // pointer transferred to params FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &UE->pduSessions) { pdusession_t *session = &item->param; + // Collect NAS PDUs if (session->nas_pdu.len > 0) { params.dedicated_NAS_msg_list[params.num_nas_msg++] = session->nas_pdu; session->nas_pdu.buf = NULL; session->nas_pdu.len = 0; LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", session->nas_pdu.len); } + // Collect DRBs to release for PDU sessions marked for release + if (item->status == PDU_SESSION_STATUS_TORELEASE) { + if (!params.drb_rel) + params.drb_rel = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(int)); + FOR_EACH_SEQ_ARR (drb_t *, drb, &UE->drbs) { + if (drb->pdusession_id == session->pdusession_id) { + if (params.n_drb_rel >= MAX_DRBS_PER_UE) { + LOG_E(NR_RRC, "UE %d: Too many DRBs to release (max %d)\n", UE->rrc_ue_id, MAX_DRBS_PER_UE); + break; + } + params.drb_rel[params.n_drb_rel++] = drb->drb_id; + LOG_D(NR_RRC, + "UE %d: Added DRB %d to release list for PDU session %d\n", + UE->rrc_ue_id, + drb->drb_id, + session->pdusession_id); + } + } + } } if (UE->nas_pdu.len > 0) { @@ -750,21 +768,24 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_ { /* do not re-establish PDCP for any bearer */ nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false); - ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH; + FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { + if (item->status == PDU_SESSION_STATUS_NEW) { + item->status = PDU_SESSION_STATUS_DONE; + continue; + } + // Set xid for all PDU sessions + item->xid = params.transaction_id; + } + + // Set xid for RRC transaction + ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH; + byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params); if (msg.len <= 0) { LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); return; } - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if (item->param.nas_pdu.buf) - item->xid = params.transaction_id; - if (item->status < PDU_SESSION_STATUS_ESTABLISHED) { - item->status = PDU_SESSION_STATUS_DONE; - } - } - LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id); const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); @@ -835,44 +856,27 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t free_byte_array(msg); } -//----------------------------------------------------------------------------- -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer) -//----------------------------------------------------------------------------- +static void rrc_gNB_send_f1_drb_release_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, int *drb_to_release, int n_drb_to_release) { - nr_rrc_reconfig_param_t params = {.transaction_id = xid}; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); + f1ap_ue_context_mod_req_t req = { + .gNB_CU_ue_id = ue_p->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + .servCellIndex = 0, + }; + req.plmn = malloc_or_fail(sizeof(*req.plmn)); + *req.plmn = rrc->configuration.plmn[0]; + req.nr_cellid = malloc_or_fail(sizeof(*req.nr_cellid)); + *req.nr_cellid = rrc->nr_cellid; + req.drbs_rel = malloc_or_fail(sizeof(*req.drbs_rel)); + req.drbs_rel_len = n_drb_to_release; + memcpy(req.drbs_rel, drb_to_release, n_drb_to_release * sizeof(int)); - NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1); - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if ((item->status != PDU_SESSION_STATUS_TORELEASE) && item->xid != xid) - continue; - FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) { - if (drb->pdusession_id == item->param.pdusession_id) { - asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); - *DRB_release = drb->drb_id; // DRB ID - } - } - } - params.drb_release_list = to_release; - - if (nas_length > 0) { - params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer; - params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length; - } - byte_array_t msg = do_RRCReconfiguration(¶ms); - if (msg.len <= 0) { - LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); - return; - } - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n"); - LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %ld)\n", ue_p->rrc_ue_id, msg.len); - const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; - nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); - free_RRCReconfiguration_params(params); - free_byte_array(msg); + /* send UE Context Modification to DU without attaching any RRC container */ + rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &req); + LOG_I(NR_RRC, "UE %d: send F1 UE Context Modification Request with DRB release (%d DRBs)\n", ue_p->rrc_ue_id, req.drbs_rel_len); + free_ue_context_mod_req(&req); } static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_information_t *secInfo) @@ -1851,9 +1855,6 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U switch (UE->xids[xid]) { case RRC_PDUSESSION_RELEASE: { - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); } break; case RRC_PDUSESSION_ESTABLISH: @@ -2732,15 +2733,19 @@ void rrc_gNB_process_e1_bearer_context_setup_failure(e1ap_bearer_context_setup_f */ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp) { + LOG_I(NR_RRC, "Received E1AP Bearer Context Modification Response for UE CU-CP ID %d\n", resp->gNB_cu_cp_ue_id); gNB_RRC_INST *rrc = RC.nrrrc[0]; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id); if (ue_context_p == NULL) { LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id); return; } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; int n_drb_mod = 0; int drb_ids[MAX_DRBS_PER_UE] = {0}; + int drb_to_release[MAX_DRBS_PER_UE] = {0}; + int n_drb_to_release = 0; e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0}; for (int i = 0; i < resp->numPDUSessionsMod; ++i) { const pdu_session_modif_t *pdu = &resp->pduSessionMod[i]; @@ -2753,13 +2758,32 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status; } } + // Collect DRBs to release for PDU sessions marked for release + rrc_pdu_session_param_t *pdu_session = find_pduSession(&ue->pduSessions, pdu->id); + if (pdu_session && pdu_session->status == PDU_SESSION_STATUS_TORELEASE) { + FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) { + if (drb->pdusession_id == pdu->id) { + DevAssert(n_drb_to_release < MAX_DRBS_PER_UE); + drb_to_release[n_drb_to_release++] = drb->drb_id; + } + } + if (n_drb_to_release == 0) { + LOG_E(NR_RRC, "UE %d: no DRBs to release for PDU session %ld\n", ue->rrc_ue_id, pdu->id); + } + } } + if (n_drb_mod) { LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id); - gNB_RRC_UE_t *ue = &ue_context_p->ue_context; if (ue->ho_context && ue->ho_context->source) ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, drb_ids, pdcp_status); } + + // Send F1 UE Context Modification Request with DRB release + if (n_drb_to_release) { + LOG_I(NR_RRC, "Send F1 UE Context Modification Request with DRB to release\n"); + rrc_gNB_send_f1_drb_release_request(rrc, ue, drb_to_release, n_drb_to_release); + } } /** @brief E1AP Bearer Context Modification Failure processing on CU-CP */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 3b30341371..570e3912fa 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1585,13 +1585,12 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ return -1; } - LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id); gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - LOG_I( - NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease); - bool found = false; - uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); - UE->xids[xid] = RRC_PDUSESSION_RELEASE; + LOG_I(NR_RRC, "PDU Session Release: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", + cmd->amf_ue_ngap_id, + gNB_ue_ngap_id, + cmd->nb_pdusessions_torelease); + e1ap_bearer_mod_req_t req = {0}; for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) { rrc_pdu_session_param_t *pduSession = find_pduSession(&UE->pduSessions, cmd->pdusession_release_params[pdusession].pdusession_id); if (!pduSession) { @@ -1599,30 +1598,35 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ continue; } if (pduSession->status == PDU_SESSION_STATUS_FAILED) { - pduSession->xid = xid; continue; } if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) { - found = true; - LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id); + LOG_I(NR_RRC, "NG Release PDU Session %d \n", pduSession->param.pdusession_id); + pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++]; + release->sessionId = pduSession->param.pdusession_id; + release->cause.type = E1AP_CAUSE_RADIO_NETWORK; + release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; pduSession->status = PDU_SESSION_STATUS_TORELEASE; - pduSession->xid = xid; } } - if (found) { - // TODO RRCReconfiguration To UE - LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n"); - rrc_gNB_generate_dedicatedRRCReconfiguration_release(rrc, UE, xid, cmd->nas_pdu.len, cmd->nas_pdu.buf); + if (req.numPDUSessionsRem == 0) { + LOG_E(NR_RRC, "Received NG PDU Session Release Command but no PDU Sessions to release\n"); + return -1; + } else if (!ue_associated_to_cuup(rrc, UE)) { + LOG_E(NR_RRC, "UE %d is not associated to CU-UP\n", UE->rrc_ue_id); + // TODO handle, e.g., only trigger F1 release + return 0; } else { - // gtp tunnel delete - LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti); - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - req.ue_id = UE->rnti; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE - rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); - LOG_I(NR_RRC, "Send PDU Session Release Response \n"); + /* If present, store NAS-PDU in the UE context for later inclusion in RRCReconfiguration */ + if (cmd->nas_pdu.len > 0) { + UE->nas_pdu = copy_byte_array(cmd->nas_pdu); + } + LOG_I(NR_RRC, "Send F1 Bearer Context Modification Request with PDU Session release \n"); + req.gNB_cu_cp_ue_id = UE->rrc_ue_id; + req.gNB_cu_up_ue_id = UE->rrc_ue_id; + sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE); + rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); } return 0; } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 389213986e..c34c453e60 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -590,14 +590,13 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n"); return false; } - LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id); if (entityPtr->ue_id == ue_id && entityPtr->pdusession_id == pdusession_id) { sdap_info.sdap_entity_llist = sdap_info.sdap_entity_llist->next_entity; if (entityPtr->pdusession_sock != -1) remove_ip_if(entityPtr); free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted SDAP entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } else { while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL @@ -613,7 +612,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) remove_ip_if(entityPtr); } free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted Entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } } diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index ab14ee6241..9f22b05b09 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -905,7 +905,7 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) } map::iterator rb_it = ue_it->second.bearers.find(rb_id); if (rb_it == ue_it->second.bearers.end()) { - LOG_E(GTPU, "%s() UE %ld has no bearer %d, available\n", __func__, ue_id, rb_id); + LOG_E(GTPU, "%s() UE %ld has no tunnel for bearer %d\n", __func__, ue_id, rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); return !GTPNOK; } @@ -913,9 +913,9 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) globGtp.te2ue_mapping.erase(teid); ue_it->second.bearers.erase(rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "Deleted tunnel TEID %d (RB %d) for ue id %ld, remaining bearers:\n", teid, rb_id, ue_id); + LOG_I(GTPU, "Deleted tunnel TEID 0x%x for bearer %d of UE %ld, remaining tunnels:\n", teid, rb_id, ue_id); for (auto b : ue_it->second.bearers) - LOG_I(GTPU, "bearer %ld\n", b.first); + LOG_I(GTPU, "Bearer %ld\n", b.first); return !GTPNOK; }