Commit a58cf178 authored by Guido Casati's avatar Guido Casati Committed by Robert Schmidt

RRC PDU Session Release: end-to-end DRB and PDU session release

This commit implements proper PDU Session Release handling across
RRC, PDCP, F1AP, E1AP to ensure clean resource cleanup when
a PDU session is terminated.

Key changes:

NGAP:

In `rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND()`:
  * Populates E1AP Bearer Modification for each released session
  * If a CU-UP is connected, triggers `bearer_context_mod()` for PDU teardown

PDCP:
 * Added `nr_pdcp_release_drbs()` to remove all DRBs linked to a given PDU session
 * Called from `e1_bearer_context_modif()` for correct clean-up during session release

F1AP UE Context Modification:

  Replace direct RRC message transfer with F1AP UE Context Modification Request
  to properly handle PDU session release according to 3GPP TS 38.331.

  3GPP TS 38.331 `5.3.5.6.4 DRB release` says: whether or not the RLC and MAC entities associated
  with a PDCP entity are reset or released is determined by the CellGroupConfig. Thus,
  updated CellGroupConfig needs to be received by the UE. This has to be retrieved by CUCP
  from the DU. Therefore:

  After receiving an NGAP_PDUSESSION_RELEASE_COMMAND, the CUCP shall:
  1) forward (store for later) the NAS PDU to the UE, if present as per 3GPP TS 38.413, and
  2) send a UE Context Modification to the DU with the list of DRBs to release
     and the RRC container (RRCReconfiguration with NAS PDU)

  After reception of UE Context Modification Response, the list of drbs to release
  is prepared based on PDU session status PDU_SESSION_STATUS_TORELEASE
  in rrc_gNB_generate_dedicatedRRCReconfiguration and then encoded
  in drb_ToReleaseList for a new RRCReconfiguration with the new cellGroupConfig.

  Key changes:
  * rrc_gNB_generate_dedicatedRRCReconfiguration_release() now uses F1AP UE Context
    Modification Request instead of direct RRC transfer
  * Only DRBs to be released are now added to the list.
  * The ASN.1 memory allocation is entirely done in build_RRCReconfiguration_IEs
  * No longer needed to store ASN1 struct in UE context

GTP-U:
  * Previous implementation had incomplete tunnel cleanup during PDU session release
    - Missing distinction between N3 (PDU session level) and F1-U (DRB level) tunnels
    - Lack of proper DRB identification for F1-U tunnel deletion
  Major Changes:
  * GTP tunnel teardown now handled in `e1_bearer_context_modif()` using the correct
  F1 or N3 GTP instance (`newGtpuDeleteOneTunnel()`)
  * Enhanced Tunnel Deletion Logic (cucp_cuup_handler.c):
   - Separate handling for N3 tunnels (1 per PDU session) and F1-U tunnels (1 per DRB)
   - Added proper DRB identification for F1-U tunnel cleanup
   - Improved error handling with detailed logging for each tunnel deletion
   - Enhanced logging to distinguish between N3 and F1-U tunnel operations
  * New PDCP API Function (nr_pdcp_oai_api.c/h):
   - Added nr_pdcp_get_drb_ids_for_pdusession() function
   - Enables retrieval of all DRB IDs associated with a specific PDU session
   - Supports proper F1-U tunnel cleanup by providing DRB identifiers in CUUP
  * Enhanced GTP Interface (gtp_itf.cpp/h):
   - Modified newGtpuDeleteOneTunnel() to accept tunnel_type parameter
   - Improved logging to distinguish between PDU session and DRB tunnel types
   - Better error messages for tunnel deletion failures
   - Enhanced debugging information for remaining tunnels

Clean-up:
* Ensured xid tracking works consistently between PDU Session Establishment (with NAS PDU)
  and dedicated RRCReconfiguration
* Add function to release GTP-U tunnel in Bearer Context Modification Request handler
* Do not send rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE when no PDU session are to
  be released: it is sent upon completion of the RRCReconfiguration and needs to contain
  released PDU Sessions (mandatory IE), therefore it is no necessary to send it in case
  there is no PDU session to release.

Related to #867
Co-authored-by: default avatarrmagueta <rmagueta@allbesmart.pt>
parent a9ad90a2
......@@ -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));
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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,
......
......@@ -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 */
......
......@@ -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];
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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};
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(&params);
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);
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));
/* 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 */
......
......@@ -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;
}
......
......@@ -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;
}
}
......
......@@ -905,7 +905,7 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id)
}
map<ue_id_t, gtpv1u_bearer_t>::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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment