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: rmagueta <rmagueta@allbesmart.pt>
This commit is contained in:
committed by
Robert Schmidt
co-authored by
rmagueta
parent
a9ad90a26a
commit
a58cf178ed
@@ -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);
|
||||
|
||||
+76
-52
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user