Merge remote-tracking branch 'origin/n2-handover-minor-fixes' into integration_2025_w40 (!3655)
The goal of this MR is to resolve memory leaks, prevent crashes, and improve code quality. Bug Fixes: - Fix crash in get_du_for_ue when F1 UE data doesn't exist during N2 handover - Fix memory leaks in handover preparation and UE context cleanup - Fix duplicate handover request handling with proper UE context validation Code Quality Improvements: - Prevent ASN.1 double-free bug in NGAP memory management (caller ownership principle) - Fix hardcoded instance 0 bug in UE context release command - Refactor e1_send_bearer_updates into three specialized functions with extracted common code - Replace impossible ID mismatch checks with DevAssert statements - Fix inconsistent equality checking patterns in E1AP bearer context management - Improve error message consistency across NGAP handover functions - Optimize PLMN identity assignment using struct copies Documentation Enhancements: - Improve RRC neighbor configuration documentation with actual parameter names - Add same-machine setup guidance for N2 handover with network configuration notes - Fix grammar errors and standardize naming conventions Follow up to !3334 (merged)
This commit is contained in:
@@ -398,7 +398,6 @@ static bool eq_pdcp_info(const e1_pdcp_status_info_t *a, const e1_pdcp_status_in
|
||||
*/
|
||||
static bool eq_drb_to_setup(const DRB_nGRAN_to_setup_t *a, const DRB_nGRAN_to_setup_t *b)
|
||||
{
|
||||
bool result = true;
|
||||
_E1_EQ_CHECK_LONG(a->id, b->id);
|
||||
_E1_EQ_CHECK_INT(a->numCellGroups, b->numCellGroups);
|
||||
for (int i = 0; i < a->numCellGroups; i++) {
|
||||
@@ -406,11 +405,14 @@ static bool eq_drb_to_setup(const DRB_nGRAN_to_setup_t *a, const DRB_nGRAN_to_se
|
||||
}
|
||||
_E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup);
|
||||
for (int i = 0; i < a->numQosFlow2Setup; i++) {
|
||||
result &= eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]);
|
||||
if (!eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]))
|
||||
return false;
|
||||
}
|
||||
result &= eq_pdcp_config(&a->pdcp_config, &b->pdcp_config);
|
||||
result &= eq_sdap_config(&a->sdap_config, &b->sdap_config);
|
||||
return result;
|
||||
if (!eq_pdcp_config(&a->pdcp_config, &b->pdcp_config))
|
||||
return false;
|
||||
if (!eq_sdap_config(&a->sdap_config, &b->sdap_config))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_drb_to_setup_item(const DRB_nGRAN_to_setup_t *msg)
|
||||
@@ -1939,32 +1941,42 @@ static bool eq_security_ind(security_indication_t *a, security_indication_t *b)
|
||||
*/
|
||||
static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t *b)
|
||||
{
|
||||
bool result = true;
|
||||
_E1_EQ_CHECK_LONG(a->id, b->id);
|
||||
_E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup);
|
||||
for (int i = 0; i < a->numQosFlow2Setup; i++) {
|
||||
result &= eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]);
|
||||
if (!eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]))
|
||||
return false;
|
||||
}
|
||||
_E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam);
|
||||
for (int i = 0; i < a->numDlUpParam; i++) {
|
||||
_E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id);
|
||||
result &= eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
|
||||
if (!eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info))
|
||||
return false;
|
||||
}
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, pdcp_config);
|
||||
if (a->pdcp_config && b->pdcp_config) {
|
||||
if (!eq_pdcp_config(a->pdcp_config, b->pdcp_config))
|
||||
return false;
|
||||
}
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, sdap_config);
|
||||
if (a->sdap_config && b->sdap_config) {
|
||||
if (!eq_sdap_config(a->sdap_config, b->sdap_config))
|
||||
return false;
|
||||
}
|
||||
if (a->pdcp_config && b->pdcp_config)
|
||||
result &= eq_pdcp_config(a->pdcp_config, b->pdcp_config);
|
||||
if (a->sdap_config && b->sdap_config)
|
||||
result &= eq_sdap_config(a->sdap_config, b->sdap_config);
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, pdcp_status);
|
||||
if (a->pdcp_status && b->pdcp_status)
|
||||
result &= eq_pdcp_info(a->pdcp_status, b->pdcp_status);
|
||||
return result;
|
||||
if (a->pdcp_status && b->pdcp_status) {
|
||||
if (!eq_pdcp_info(a->pdcp_status, b->pdcp_status))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Equality check for PDU session item to modify */
|
||||
static bool eq_pdu_session_to_mod_item(const pdu_session_to_mod_t *a, const pdu_session_to_mod_t *b)
|
||||
{
|
||||
_E1_EQ_CHECK_LONG(a->sessionId, b->sessionId);
|
||||
if (a->UP_TL_information && a->UP_TL_information) {
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, UP_TL_information);
|
||||
if (a->UP_TL_information && b->UP_TL_information) {
|
||||
if (!eq_up_tl_info(a->UP_TL_information, b->UP_TL_information))
|
||||
return false;
|
||||
}
|
||||
@@ -1978,8 +1990,10 @@ static bool eq_pdu_session_to_mod_item(const pdu_session_to_mod_t *a, const pdu_
|
||||
if (!eq_drb_to_mod(&a->DRBnGRanModList[i], &b->DRBnGRanModList[i]))
|
||||
return false;
|
||||
}
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, securityIndication);
|
||||
if (a->securityIndication && b->securityIndication) {
|
||||
eq_security_ind(a->securityIndication, b->securityIndication);
|
||||
if (!eq_security_ind(a->securityIndication, b->securityIndication))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2423,8 +2437,6 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
{
|
||||
if (!a || !b) return false; // Null-check both inputs
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Basic members
|
||||
_E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id);
|
||||
_E1_EQ_CHECK_INT(a->gNB_cu_up_ue_id, b->gNB_cu_up_ue_id);
|
||||
@@ -2440,8 +2452,10 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
_E1_EQ_CHECK_OPTIONAL_IE(pduA, pduB, integrityProtectionIndication, _E1_EQ_CHECK_INT);
|
||||
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(pduA, pduB, ng_DL_UP_TL_info);
|
||||
if (pduA->ng_DL_UP_TL_info && pduB->ng_DL_UP_TL_info)
|
||||
result &= eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
|
||||
if (pduA->ng_DL_UP_TL_info && pduB->ng_DL_UP_TL_info) {
|
||||
if (!eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info))
|
||||
return false;
|
||||
}
|
||||
|
||||
// DRB Modified List
|
||||
_E1_EQ_CHECK_INT(pduA->numDRBModified, pduB->numDRBModified);
|
||||
@@ -2455,7 +2469,8 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
}
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(drbModA, drbModB, pdcp_status);
|
||||
if (drbModA->pdcp_status && drbModB->pdcp_status) {
|
||||
result &= eq_pdcp_info(drbModA->pdcp_status, drbModA->pdcp_status);
|
||||
if (!eq_pdcp_info(drbModA->pdcp_status, drbModB->pdcp_status))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2490,7 +2505,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -721,6 +721,7 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
/* free the memory we allocated above */
|
||||
free_ue_context_setup_resp(&resp);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CG_ConfigInfo, cg_configinfo);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
|
||||
}
|
||||
|
||||
void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
|
||||
#define NR_MAX_SUPPORTED_DL_LAYERS 4
|
||||
|
||||
/* Default values for measurement gap configuration */
|
||||
#define DEFAULT_MGRP NR_GapConfig__mgrp_ms160
|
||||
#define DEFAULT_MGTA NR_GapConfig__mgta_ms0dot5
|
||||
#define DEFAULT_MGL NR_GapConfig__mgl_ms6
|
||||
|
||||
#define PUCCH2_SIZE 8
|
||||
const uint8_t slotsperframe[5] = {10, 20, 40, 80, 160};
|
||||
|
||||
@@ -4211,53 +4216,6 @@ NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t u
|
||||
return reconfigurationWithSync;
|
||||
}
|
||||
|
||||
static NR_MeasGapConfig_t *get_gap_config_from_smtc(const NR_SSB_MTC_t *ssb_mtc)
|
||||
{
|
||||
NR_MeasGapConfig_t *measGapConfig = calloc_or_fail(1, sizeof(*measGapConfig));
|
||||
measGapConfig->ext1 = calloc_or_fail(1, sizeof(*measGapConfig->ext1));
|
||||
measGapConfig->ext1->gapUE = calloc_or_fail(1, sizeof(*measGapConfig->ext1->gapUE));
|
||||
measGapConfig->ext1->gapUE->present = NR_SetupRelease_GapConfig_PR_setup;
|
||||
NR_GapConfig_t *gap_config = calloc_or_fail(1, sizeof(*gap_config));
|
||||
measGapConfig->ext1->gapUE->choice.setup = gap_config;
|
||||
|
||||
// mgta = Measurement Gap Timing Advance, to provide sufficient time for the UE to re-tune its transceiver
|
||||
// This allows the Measurement Gap to extend mgta ms either side of the SS/PBCH Measurement Window.
|
||||
gap_config->mgta = NR_GapConfig__mgta_ms0dot5;
|
||||
|
||||
// mgrp = Measurement Gap Repetition Period
|
||||
// gapOffset = It defines the start of the Measurement Gaps relative to the start of the radio frame with SFN = 0
|
||||
// The Measurement Gaps need to be synchronized with the SS/PBCH transmissions which are to be measured
|
||||
gap_config->mgrp = NR_GapConfig__mgrp_ms160;
|
||||
switch (ssb_mtc->periodicityAndOffset.present) {
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf20:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf20;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf40:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf40;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf80:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf80;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf160:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf160;
|
||||
break;
|
||||
default:
|
||||
LOG_W(NR_RRC, "SMTC periodicity higher than MGRP\n");
|
||||
// With this configuration, not all SSBs belong to the Measurement Gap.
|
||||
if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf5) {
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf5;
|
||||
} else if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf10) {
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf10;
|
||||
}
|
||||
}
|
||||
|
||||
// mgl = Measurement Gap Length
|
||||
// FIXME: At least the duration of the SMTC plus 2 times the mgta should be enough,
|
||||
// however, at the moment it only works by setting the maximum value
|
||||
gap_config->mgl = NR_GapConfig__mgl_ms6;
|
||||
|
||||
return measGapConfig;
|
||||
}
|
||||
|
||||
NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
@@ -4330,25 +4288,37 @@ static float get_mgta(long mgta)
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Return Measurement Gap Configuration, from ASN.1 config */
|
||||
const NR_GapConfig_t *get_gap_config(const NR_MeasGapConfig_t *mgc)
|
||||
/** @brief Extract gapOffset from SSB MTC periodicity */
|
||||
static bool extract_gap_offset_from_smtc(const NR_SSB_MTC_t *ssb_mtc, long *gapOffset)
|
||||
{
|
||||
if (mgc->gapFR2 && mgc->gapFR2->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return mgc->gapFR2->choice.setup;
|
||||
if (!ssb_mtc)
|
||||
return false;
|
||||
|
||||
// FR1 case
|
||||
if (!mgc->ext1)
|
||||
return NULL;
|
||||
// Extract gapOffset based on periodicity
|
||||
switch (ssb_mtc->periodicityAndOffset.present) {
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf20:
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf20;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf40:
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf40;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf80:
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf80;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf160:
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf160;
|
||||
break;
|
||||
default:
|
||||
LOG_W(NR_RRC, "SMTC periodicity higher than MGRP\n");
|
||||
if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf5) {
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf5;
|
||||
} else if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf10) {
|
||||
*gapOffset = ssb_mtc->periodicityAndOffset.choice.sf10;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const NR_SetupRelease_GapConfig_t *gapUE = mgc->ext1->gapUE;
|
||||
if (gapUE && gapUE->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return gapUE->choice.setup;
|
||||
|
||||
const NR_SetupRelease_GapConfig_t *gapFR1 = mgc->ext1->gapFR1;
|
||||
if (gapFR1 && gapFR1->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return gapFR1->choice.setup;
|
||||
|
||||
return NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime)
|
||||
@@ -4359,18 +4329,25 @@ measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t
|
||||
DevAssert(mt != NULL && mt->frequencyAndTiming != NULL);
|
||||
const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming;
|
||||
const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration;
|
||||
NR_MeasGapConfig_t *measGapConfig = get_gap_config_from_smtc(ssb_mtc);
|
||||
const NR_GapConfig_t *gap_config = get_gap_config(measGapConfig);
|
||||
if (!gap_config)
|
||||
|
||||
// Initialize with defaults
|
||||
long mgrp = DEFAULT_MGRP;
|
||||
long gapOffset = 0;
|
||||
long mgta = DEFAULT_MGTA;
|
||||
long mgl = DEFAULT_MGL;
|
||||
|
||||
// Extract gapOffset from SSB MTC periodicity
|
||||
if (!extract_gap_offset_from_smtc(ssb_mtc, &gapOffset)) {
|
||||
return mgc;
|
||||
}
|
||||
|
||||
mgc.mgrp_ms = get_mgrp(gap_config->mgrp);
|
||||
mgc.mgrp_ms = get_mgrp(mgrp);
|
||||
DevAssert(mgc.mgrp_ms != -1);
|
||||
mgc.mgrp = gap_config->mgrp;
|
||||
mgc.mgrp = mgrp;
|
||||
|
||||
mgc.gapOffset = gap_config->gapOffset;
|
||||
mgc.mgta = gap_config->mgta;
|
||||
mgc.n_slots_mgta = ((int)(10 * get_mgta(gap_config->mgta)) << scs) / 10;
|
||||
mgc.gapOffset = gapOffset;
|
||||
mgc.mgta = mgta;
|
||||
mgc.n_slots_mgta = ((int)(10 * get_mgta(mgta)) << scs) / 10;
|
||||
|
||||
// We start the timer K2 slots earlier to avoid scheduling feedback PUCCHs inside measGap
|
||||
// or depending on the current min_rxtxtime earlier
|
||||
@@ -4379,12 +4356,13 @@ measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t
|
||||
|
||||
mgc.n_slots_advance = mgc.n_slots_mgta + max_k2;
|
||||
|
||||
mgc.mgl_ms = get_mgl(gap_config->mgl);
|
||||
mgc.mgl_ms = get_mgl(mgl);
|
||||
DevAssert(mgc.mgl_ms != -1);
|
||||
mgc.mgl = gap_config->mgl;
|
||||
mgc.mgl = mgl;
|
||||
mgc.mgl_slots = ((int)(10 * (mgc.mgl_ms + max_k2)) << scs) / 10;
|
||||
|
||||
mgc.enable = true;
|
||||
|
||||
return mgc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1145,7 +1145,11 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
if (rc_A3_seq) {
|
||||
for (int i = 0; i < rc_A3_seq->size; i++) {
|
||||
NR_ReportConfigToAddMod_t *rc_A3 = (NR_ReportConfigToAddMod_t *)seq_arr_at(rc_A3_seq, i);
|
||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A3);
|
||||
// Create a deep copy of the report config
|
||||
NR_ReportConfigToAddMod_t *rc_A3_copy = NULL;
|
||||
int result = asn_copy(&asn_DEF_NR_ReportConfigToAddMod, (void **)&rc_A3_copy, rc_A3);
|
||||
AssertFatal(result >= 0, "error during asn_copy() of ReportConfigToAddMod\n");
|
||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A3_copy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1255,6 +1259,7 @@ void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_
|
||||
and extract the measConfig provided by the source gNB */
|
||||
if (!hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig) {
|
||||
LOG_W(NR_RRC, "Missing sourceConfig: in source gNB rrcReconfiguration\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||
return;
|
||||
}
|
||||
NR_RRCReconfiguration_t *rrcReconf = NULL;
|
||||
@@ -1266,11 +1271,15 @@ void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_
|
||||
sourceConfig->rrcReconfiguration.size);
|
||||
if (rrcReconf_dec_rval.code != RC_OK || rrcReconf_dec_rval.consumed < 0) {
|
||||
LOG_E(NR_RRC, "Failed to decode source gNB rrcReconfiguration!\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL)
|
||||
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, rrcReconf);
|
||||
return;
|
||||
}
|
||||
|
||||
NR_MeasConfig_t *sourceMC = rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig;
|
||||
|
||||
@@ -1302,6 +1311,10 @@ void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_
|
||||
asn1cSeqAdd(¤tMC->measIdToRemoveList->list, measId);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up allocated memory
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, rrcReconf);
|
||||
}
|
||||
|
||||
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand)
|
||||
|
||||
@@ -127,6 +127,6 @@ void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *c
|
||||
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish);
|
||||
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering);
|
||||
void activate_srb(gNB_RRC_UE_t *UE, int srb_id);
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status);
|
||||
void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status);
|
||||
|
||||
#endif
|
||||
|
||||
+184
-130
@@ -638,11 +638,14 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
nr_rrc_du_container_t *du = get_du_by_cell_id((gNB_RRC_INST *)rrc, nr_cellid);
|
||||
DevAssert(du != NULL);
|
||||
f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
|
||||
NR_ReportConfigToAddMod_t *rc_PER = NULL;
|
||||
NR_ReportConfigToAddMod_t *rc_A2 = NULL;
|
||||
seq_arr_t *rc_A3_seq = NULL;
|
||||
seq_arr_t *neigh_seq = NULL;
|
||||
|
||||
if (du->mtc != NULL) {
|
||||
NR_ReportConfigToAddMod_t *rc_PER = NULL;
|
||||
NR_ReportConfigToAddMod_t *rc_A2 = NULL;
|
||||
seq_arr_t rc_A3_seq = {0};
|
||||
seq_arr_t neigh_seq = {0};
|
||||
seq_arr_init(&rc_A3_seq, sizeof(NR_ReportConfigToAddMod_t));
|
||||
seq_arr_init(&neigh_seq, sizeof(nr_neighbour_cell_t));
|
||||
int scs = get_ssb_scs(cell_info);
|
||||
int band = get_dl_band(cell_info);
|
||||
const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
|
||||
@@ -657,21 +660,17 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
If no related A3 but there is default add the default one.
|
||||
If default one added once as a report, no need to add it again && duplication.
|
||||
*/
|
||||
rc_A3_seq = malloc(sizeof(seq_arr_t));
|
||||
neigh_seq = malloc(sizeof(seq_arr_t));
|
||||
seq_arr_init(rc_A3_seq, sizeof(NR_ReportConfigToAddMod_t));
|
||||
seq_arr_init(neigh_seq, sizeof(nr_neighbour_cell_t));
|
||||
LOG_D(NR_RRC, "Preparing A3 Event Measurement Configuration!\n");
|
||||
bool default_a3_added = false; // To ensure that the default configuration is only added once
|
||||
for (int i = 0; i < neighbour_cells->size; i++) {
|
||||
nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i);
|
||||
if (default_a3_added && neighbourCell->physicalCellId == -1)
|
||||
continue;
|
||||
seq_arr_push_back(neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t));
|
||||
seq_arr_push_back(&neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t));
|
||||
const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId);
|
||||
if (a3Event) {
|
||||
NR_ReportConfigId_t reportConfigId = neighbourCell->physicalCellId == -1 ? 3 : i + 4;
|
||||
seq_arr_push_back(rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t));
|
||||
seq_arr_push_back(&rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t));
|
||||
if (neighbourCell->physicalCellId == -1)
|
||||
default_a3_added = true;
|
||||
}
|
||||
@@ -681,7 +680,14 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event);
|
||||
if (rrc->measurementConfiguration.a2_event)
|
||||
rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event);
|
||||
return get_MeasConfig(mt, band, scs, cell_info->nr_pci, rc_PER, rc_A2, rc_A3_seq, neigh_seq);
|
||||
|
||||
NR_MeasConfig_t *result = get_MeasConfig(mt, band, scs, cell_info->nr_pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq);
|
||||
|
||||
// Clean up sequence arrays
|
||||
seq_arr_free(&rc_A3_seq, NULL);
|
||||
seq_arr_free(&neigh_seq, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -873,22 +879,65 @@ static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_inf
|
||||
nr_derive_key(UP_INT_ALG, secInfo->integrityProtectionAlgorithm, UE->kgnb, (uint8_t *)secInfo->integrityProtectionKey);
|
||||
}
|
||||
|
||||
/* \brief find existing PDU session inside E1AP Bearer Modif message, or
|
||||
* point to new one.
|
||||
* \param bearer_modif E1AP Bearer Modification Message
|
||||
* \param pdu_id PDU session ID
|
||||
* \return pointer to existing PDU session, or to new/unused one. */
|
||||
static pdu_session_to_mod_t *find_or_next_pdu_session(e1ap_bearer_mod_req_t *bearer_modif, int pdu_id)
|
||||
/** @brief Add DRB modification to E1AP bearer modification request
|
||||
* Finds PDU session by DRB ID, creates/updates PDU session modification entry
|
||||
* and appends DRB modification
|
||||
* @param req E1AP bearer modification request
|
||||
* @param ue UE context
|
||||
* @param drb_id DRB ID to find PDU session for
|
||||
* @param drb_to_mod DRB modification data to append
|
||||
* @return true if successful, false if PDU session not found */
|
||||
static bool append_e1_drb_mod_req(e1ap_bearer_mod_req_t *req,
|
||||
gNB_RRC_UE_t *ue,
|
||||
const int drb_id,
|
||||
const DRB_nGRAN_to_mod_t *drb_to_mod)
|
||||
{
|
||||
for (int i = 0; i < bearer_modif->numPDUSessionsMod; ++i) {
|
||||
if (bearer_modif->pduSessionMod[i].sessionId == pdu_id)
|
||||
return &bearer_modif->pduSessionMod[i];
|
||||
// Find PDU session by DRB ID
|
||||
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, drb_id);
|
||||
if (!pdu) {
|
||||
LOG_E(NR_RRC, "UE %d: Failed to append E1 DRB mod, no PDU session found (DRB=%d)\n", ue->rrc_ue_id, drb_id);
|
||||
return false;
|
||||
}
|
||||
/* E1AP Bearer Modification has no PDU session to modify with that ID, create
|
||||
* new entry */
|
||||
DevAssert(bearer_modif->numPDUSessionsMod < E1AP_MAX_NUM_PDU_SESSIONS - 1);
|
||||
bearer_modif->numPDUSessionsMod += 1;
|
||||
return &bearer_modif->pduSessionMod[bearer_modif->numPDUSessionsMod - 1];
|
||||
|
||||
int pdu_id = pdu->param.pdusession_id;
|
||||
|
||||
// Find existing PDU session modification entry or create new one
|
||||
pdu_session_to_mod_t *pdu_mod = NULL;
|
||||
for (int i = 0; i < req->numPDUSessionsMod; ++i) {
|
||||
if (req->pduSessionMod[i].sessionId == pdu_id) {
|
||||
pdu_mod = &req->pduSessionMod[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new PDU session modification entry if not found
|
||||
if (!pdu_mod) {
|
||||
DevAssert(req->numPDUSessionsMod < E1AP_MAX_NUM_PDU_SESSIONS);
|
||||
int new_index = req->numPDUSessionsMod++;
|
||||
req->pduSessionMod[new_index].sessionId = pdu_id;
|
||||
pdu_mod = &req->pduSessionMod[new_index];
|
||||
}
|
||||
|
||||
// Append DRB modification
|
||||
DevAssert(pdu_mod->numDRB2Modify < E1AP_MAX_NUM_DRBS);
|
||||
pdu_mod->DRBnGRanModList[pdu_mod->numDRB2Modify++] = *drb_to_mod;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Create DRB modification for reestablishment from existing DRB */
|
||||
static DRB_nGRAN_to_mod_t get_e1_drb_mod_reestablishment(const drb_t *drb, const bearer_context_pdcp_config_t *pdcp_config)
|
||||
{
|
||||
DRB_nGRAN_to_mod_t drb_e1 = {0};
|
||||
drb_e1.id = drb->drb_id;
|
||||
drb_e1.numDlUpParam = 1;
|
||||
memcpy(&drb_e1.DlUpParamList[0].tl_info.tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4);
|
||||
drb_e1.DlUpParamList[0].tl_info.teId = drb->du_tunnel_config.teid;
|
||||
/* PDCP configuration */
|
||||
drb_e1.pdcp_config = malloc_or_fail(sizeof(*drb_e1.pdcp_config));
|
||||
*drb_e1.pdcp_config = *pdcp_config;
|
||||
drb_e1.pdcp_config->pDCP_Reestablishment = true;
|
||||
return drb_e1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -901,44 +950,18 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
|
||||
.gNB_cu_up_ue_id = ue_p->rrc_ue_id,
|
||||
};
|
||||
// Quit re-establishment notification if no CU-UP is associated
|
||||
if (!is_cuup_associated(rrc)) {
|
||||
if (!is_cuup_associated(rrc) || !ue_associated_to_cuup(rrc, ue_p)) {
|
||||
return;
|
||||
}
|
||||
if (!ue_associated_to_cuup(rrc, ue_p))
|
||||
return;
|
||||
|
||||
bool um_on_default_drb = rrc->configuration.um_on_default_drb;
|
||||
/* loop through active DRBs */
|
||||
for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
|
||||
drb_t *drb = get_drb(&ue_p->drbs, drb_id);
|
||||
if (!drb)
|
||||
continue;
|
||||
/* fetch an existing PDU session for this DRB */
|
||||
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id);
|
||||
if (pdu == NULL) {
|
||||
LOG_E(RRC, "UE %d: E1 Bearer Context Modification: no PDU session for DRB ID %d\n", ue_p->rrc_ue_id, drb_id);
|
||||
continue;
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) {
|
||||
bearer_context_pdcp_config_t pdcp = set_bearer_context_pdcp_config(drb->pdcp_config, um_on_default_drb, ue_p->redcap_cap);
|
||||
DRB_nGRAN_to_mod_t drb_e1 = get_e1_drb_mod_reestablishment(drb, &pdcp);
|
||||
if (!append_e1_drb_mod_req(&req, ue_p, drb->drb_id, &drb_e1)) {
|
||||
LOG_W(NR_RRC, "UE %d: Failed to append E1 DRB mod for reestablishment (DRB=%d)\n", ue_p->rrc_ue_id, drb->drb_id);
|
||||
}
|
||||
/* Get pointer to existing (or new one) PDU session to modify in E1 */
|
||||
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
|
||||
AssertError(pdu != NULL,
|
||||
continue,
|
||||
"UE %u: E1 Bearer Context Modification: PDU session %d to setup is null\n",
|
||||
ue_p->rrc_ue_id,
|
||||
pdu->param.pdusession_id);
|
||||
/* Prepare PDU for E1 Bearear Context Modification Request */
|
||||
pdu_e1->sessionId = pdu->param.pdusession_id;
|
||||
/* Fill DRB to setup with ID, DL TL and DL TEID */
|
||||
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
|
||||
drb_e1->id = drb_id;
|
||||
drb_e1->numDlUpParam = 1;
|
||||
memcpy(&drb_e1->DlUpParamList[0].tl_info.tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4);
|
||||
drb_e1->DlUpParamList[0].tl_info.teId = drb->du_tunnel_config.teid;
|
||||
/* PDCP configuration */
|
||||
if (!drb_e1->pdcp_config)
|
||||
drb_e1->pdcp_config = malloc_or_fail(sizeof(*drb_e1->pdcp_config));
|
||||
*drb_e1->pdcp_config = set_bearer_context_pdcp_config(drb->pdcp_config, rrc->configuration.um_on_default_drb, ue_p->redcap_cap);
|
||||
drb_e1->pdcp_config->pDCP_Reestablishment = true;
|
||||
/* increase DRB to modify counter */
|
||||
pdu_e1->numDRB2Modify += 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -2088,12 +2111,14 @@ void rrc_gNB_process_dc_overall_timeout(const module_id_t gnb_mod_idP, x2ap_ENDC
|
||||
/* \brief fill E1 bearer modification's DRB from F1 DRB
|
||||
* \param drb_e1 pointer to a DRB inside an E1 bearer modification message
|
||||
* \param drb_f1 pointer to a DRB inside an F1 UE Ctxt modification Response */
|
||||
static void fill_e1_bearer_modif(DRB_nGRAN_to_mod_t *drb_e1, const f1ap_drb_setup_t *drb_f1)
|
||||
static DRB_nGRAN_to_mod_t fill_e1_bearer_modif_from_f1(const f1ap_drb_setup_t *drb_f1)
|
||||
{
|
||||
drb_e1->id = drb_f1->id;
|
||||
drb_e1->numDlUpParam = drb_f1->up_dl_tnl_len;
|
||||
drb_e1->DlUpParamList[0].tl_info.tlAddress = drb_f1->up_dl_tnl[0].tl_address;
|
||||
drb_e1->DlUpParamList[0].tl_info.teId = drb_f1->up_dl_tnl[0].teid;
|
||||
DRB_nGRAN_to_mod_t drb_e1 = {0};
|
||||
drb_e1.id = drb_f1->id;
|
||||
drb_e1.numDlUpParam = drb_f1->up_dl_tnl_len;
|
||||
drb_e1.DlUpParamList[0].tl_info.tlAddress = drb_f1->up_dl_tnl[0].tl_address;
|
||||
drb_e1.DlUpParamList[0].tl_info.teId = drb_f1->up_dl_tnl[0].teid;
|
||||
return drb_e1;
|
||||
}
|
||||
|
||||
static gtpu_tunnel_t f1u_gtp_update(uint32_t teid, const in_addr_t addr)
|
||||
@@ -2120,89 +2145,113 @@ static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_e1_bearer_modif_pdcp_status(gNB_RRC_UE_t *UE,
|
||||
DRB_nGRAN_to_mod_t *drb_to_mod,
|
||||
const int drb_id,
|
||||
const bool um_on_default_drb,
|
||||
const ngap_drb_status_t *drb_status)
|
||||
static DRB_nGRAN_to_mod_t get_e1_drb_mod_pdcp_status(const drb_t *drb,
|
||||
bearer_context_pdcp_config_t *pdcp_config,
|
||||
const ngap_drb_status_t *drb_status)
|
||||
{
|
||||
DevAssert(drb_status);
|
||||
drb_to_mod->id = drb_id;
|
||||
drb_to_mod->pdcp_sn_status_requested = false;
|
||||
DevAssert(pdcp_config);
|
||||
DevAssert(drb);
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = {0};
|
||||
drb_to_mod.id = drb->drb_id;
|
||||
drb_to_mod.pdcp_sn_status_requested = false;
|
||||
// PDCP SN Status Information
|
||||
drb_to_mod->pdcp_config = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_config));
|
||||
drb_t *drb = get_drb(&UE->drbs, drb_id);
|
||||
*drb_to_mod->pdcp_config = set_bearer_context_pdcp_config(drb->pdcp_config, um_on_default_drb, UE->redcap_cap);
|
||||
drb_to_mod->pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_status));
|
||||
drb_to_mod->pdcp_status->dl_count.hfn = drb_status->dl_count.hfn;
|
||||
drb_to_mod->pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn;
|
||||
drb_to_mod->pdcp_status->ul_count.hfn = drb_status->ul_count.hfn;
|
||||
drb_to_mod->pdcp_status->ul_count.sn = drb_status->ul_count.pdcp_sn;
|
||||
drb_to_mod.pdcp_config = calloc_or_fail(1, sizeof(*drb_to_mod.pdcp_config));
|
||||
*drb_to_mod.pdcp_config = *pdcp_config;
|
||||
drb_to_mod.pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod.pdcp_status));
|
||||
drb_to_mod.pdcp_status->dl_count.hfn = drb_status->dl_count.hfn;
|
||||
drb_to_mod.pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn;
|
||||
drb_to_mod.pdcp_status->ul_count.hfn = drb_status->ul_count.hfn;
|
||||
drb_to_mod.pdcp_status->ul_count.sn = drb_status->ul_count.pdcp_sn;
|
||||
return drb_to_mod;
|
||||
}
|
||||
|
||||
/** @brief Fill and send Bearer Context Modification Request with:
|
||||
* (1) from F1 UE Context DRB to setup list, or (2) with PDCP Status Request */
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status)
|
||||
/** @brief Send E1 bearer context modification request to CU-UP */
|
||||
static void e1_send_bearer_modification_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, e1ap_bearer_mod_req_t *req)
|
||||
{
|
||||
// Quit bearer updates if no CU-UP is associated
|
||||
if (!is_cuup_associated(rrc)) {
|
||||
if (req->numPDUSessionsMod == 0) {
|
||||
LOG_W(NR_RRC, "UE %d: No PDU sessions to modify in E1 bearer update\n", UE->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// we assume the same UE ID in CU-UP and CU-CP
|
||||
DevAssert(req->numPDUSessions == 0);
|
||||
req->secInfo = malloc_or_fail(sizeof(*req->secInfo));
|
||||
fill_security_info(rrc, UE, req->secInfo);
|
||||
|
||||
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
|
||||
rrc->cucp_cuup.bearer_context_mod(assoc_id, req);
|
||||
}
|
||||
|
||||
/** @brief Send E1 bearer updates for DRBs to setup from F1 UE Context Modification Response */
|
||||
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
|
||||
{
|
||||
if (!is_cuup_associated(rrc) || n <= 0)
|
||||
return;
|
||||
|
||||
e1ap_bearer_mod_req_t req = {
|
||||
.gNB_cu_cp_ue_id = UE->rrc_ue_id,
|
||||
.gNB_cu_up_ue_id = UE->rrc_ue_id,
|
||||
};
|
||||
|
||||
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
|
||||
int drb_index = 0;
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
|
||||
int drb_id = drb->drb_id;
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = {0};
|
||||
if (n > 0) {
|
||||
/* DRBs to Setup in F1 UE Context Modifcation Response */
|
||||
if (drb_index == n)
|
||||
break;
|
||||
const f1ap_drb_setup_t *drb_f1 = &drbs[drb_index];
|
||||
drb_id = drb_f1->id;
|
||||
fill_e1_bearer_modif(&drb_to_mod, drb_f1);
|
||||
} else if (is_inter_cu_ho) {
|
||||
/* On-going handover: send PDCP Status Request */
|
||||
drb_t *drb = get_drb(&UE->drbs, drb_id);
|
||||
if (!drb) {
|
||||
LOG_E(NR_RRC, "Failed to send PDCP Status Request: DRB %d not found\n", drb_id);
|
||||
continue;
|
||||
}
|
||||
drb_to_mod.id = drb_id;
|
||||
// PDCP SN Status Request
|
||||
drb_to_mod.pdcp_sn_status_requested = true;
|
||||
LOG_I(NR_RRC, "PDCP Status requested (drb_id=%d)\n", drb_id);
|
||||
} else if (drb_status) {
|
||||
/* On-going handover: send PDCP Status */
|
||||
fill_e1_bearer_modif_pdcp_status(UE, &drb_to_mod, drb_id, rrc->configuration.um_on_default_drb, drb_status);
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
|
||||
int drb_id = drb_f1->id;
|
||||
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(UE, drb_id);
|
||||
if (!pdu)
|
||||
continue;
|
||||
pdu_session_to_mod_t *to_mod = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
|
||||
DevAssert(to_mod);
|
||||
to_mod->sessionId = pdu->param.pdusession_id;
|
||||
// Fill E1 DRB to Modify item
|
||||
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
|
||||
|
||||
drb_index++;
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = fill_e1_bearer_modif_from_f1(drb_f1);
|
||||
if (!append_e1_drb_mod_req(&req, UE, drb_id, &drb_to_mod)) {
|
||||
LOG_W(NR_RRC, "UE %d: Failed to append E1 DRB mod from F1 (DRB=%d)\n", UE->rrc_ue_id, drb_id);
|
||||
}
|
||||
}
|
||||
DevAssert(req.numPDUSessionsMod > 0);
|
||||
DevAssert(req.numPDUSessions == 0);
|
||||
|
||||
// Always send security information
|
||||
req.secInfo = malloc_or_fail(sizeof(*req.secInfo));
|
||||
fill_security_info(rrc, UE, req.secInfo);
|
||||
e1_send_bearer_modification_request(rrc, UE, &req);
|
||||
free_e1ap_context_mod_request(&req);
|
||||
}
|
||||
|
||||
// send the E1 bearer modification request message to update F1-U tunnel info
|
||||
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
|
||||
rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
|
||||
/** @brief Request PDCP status from CU-UP during inter-CU handover */
|
||||
static void e1_request_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
if (!is_cuup_associated(rrc))
|
||||
return;
|
||||
|
||||
e1ap_bearer_mod_req_t req = {
|
||||
.gNB_cu_cp_ue_id = UE->rrc_ue_id,
|
||||
.gNB_cu_up_ue_id = UE->rrc_ue_id,
|
||||
};
|
||||
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = {.id = drb->drb_id, .pdcp_sn_status_requested = true};
|
||||
LOG_D(NR_RRC, "PDCP Status requested (drb_id=%d)\n", drb->drb_id);
|
||||
if (!append_e1_drb_mod_req(&req, UE, drb->drb_id, &drb_to_mod)) {
|
||||
LOG_W(NR_RRC, "UE %d: Failed to append E1 DRB mod for PDCP status request (DRB=%d)\n", UE->rrc_ue_id, drb->drb_id);
|
||||
}
|
||||
}
|
||||
|
||||
e1_send_bearer_modification_request(rrc, UE, &req);
|
||||
free_e1ap_context_mod_request(&req);
|
||||
}
|
||||
|
||||
/** @brief Notify CU-UP with PDCP status during handover */
|
||||
void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status)
|
||||
{
|
||||
if (!is_cuup_associated(rrc) || !drb_status)
|
||||
return;
|
||||
|
||||
e1ap_bearer_mod_req_t req = {
|
||||
.gNB_cu_cp_ue_id = UE->rrc_ue_id,
|
||||
.gNB_cu_up_ue_id = UE->rrc_ue_id,
|
||||
};
|
||||
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
|
||||
LOG_I(NR_RRC, "Forward PDCP Status to CU-UP (drb_id=%d)\n", drb->drb_id);
|
||||
bearer_context_pdcp_config_t pdcp_config = set_bearer_context_pdcp_config(drb->pdcp_config, rrc->configuration.um_on_default_drb, UE->redcap_cap);
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = get_e1_drb_mod_pdcp_status(drb, &pdcp_config, drb_status);
|
||||
append_e1_drb_mod_req(&req, UE, drb->drb_id, &drb_to_mod);
|
||||
}
|
||||
|
||||
e1_send_bearer_modification_request(rrc, UE, &req);
|
||||
free_e1ap_context_mod_request(&req);
|
||||
}
|
||||
|
||||
static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance_t instance)
|
||||
@@ -2256,7 +2305,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
|
||||
AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
|
||||
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
|
||||
if (num_drb == 0)
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
|
||||
else
|
||||
cuup_notify_reestablishment(rrc, UE);
|
||||
}
|
||||
@@ -2332,6 +2381,9 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as
|
||||
|
||||
static void rrc_delete_ue_data(gNB_RRC_UE_t *UE)
|
||||
{
|
||||
/* Clean up handover context if it exists */
|
||||
if (UE->ho_context)
|
||||
nr_rrc_finalize_ho(UE);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults);
|
||||
@@ -2388,9 +2440,11 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
|
||||
if (resp->drbs_len > 0 || is_inter_cu_ho) {
|
||||
if (resp->drbs_len > 0) { // DRB to setup
|
||||
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
|
||||
} else if (is_inter_cu_ho) { // PDCP status request
|
||||
e1_request_pdcp_status(rrc, UE);
|
||||
}
|
||||
|
||||
if (resp->du_to_cu_rrc_info) {
|
||||
|
||||
@@ -1129,11 +1129,23 @@ void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure
|
||||
/** @brief Process NG Handover Request message (8.4.2.2 3GPP TS 38.413) */
|
||||
int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg)
|
||||
{
|
||||
LOG_I(NR_RRC, "Received Handover Request (on PCI=%lu) \n", msg->nr_cell_id);
|
||||
// Check if UE context already exists for this AMF UE NGAP ID
|
||||
rrc_gNB_ue_context_t *existing_ue_context = rrc_gNB_get_ue_context_by_amf_ue_ngap_id(rrc, msg->amf_ue_ngap_id);
|
||||
if (existing_ue_context != NULL) {
|
||||
LOG_E(RRC, "UE context already exists for AMF UE NGAP ID %ld, cannot process handover request\n", msg->amf_ue_ngap_id);
|
||||
ngap_handover_failure_t fail = {
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
|
||||
};
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, msg->nr_cell_id);
|
||||
if (du == NULL) {
|
||||
/* Cell Not Found! Return HO Request Failure*/
|
||||
LOG_E(RRC, "Failed to process Handover Request: no DU found with PCI=%lu \n", msg->nr_cell_id);
|
||||
LOG_E(RRC, "Failed to process Handover Request: no DU found with NR Cell ID=%lu \n", msg->nr_cell_id);
|
||||
ngap_handover_failure_t fail = {
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
@@ -1142,6 +1154,8 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
return -1;
|
||||
}
|
||||
uint16_t pci = du->setup_req->cell[0].info.nr_pci;
|
||||
LOG_I(NR_RRC, "Received Handover Request (on NR Cell ID=%lu, PCI=%u) \n", msg->nr_cell_id, pci);
|
||||
|
||||
// Create UE context
|
||||
sctp_assoc_t curr_assoc_id = du->assoc_id;
|
||||
@@ -1149,10 +1163,6 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
// allocate context for target
|
||||
if (UE->ho_context != NULL) {
|
||||
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", UE->rrc_ue_id);
|
||||
return -1;
|
||||
}
|
||||
UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET);
|
||||
UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target;
|
||||
|
||||
@@ -1195,12 +1205,15 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
|
||||
if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, to_setup, msg->ue_ambr.br_dl)) {
|
||||
LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\n");
|
||||
|
||||
ngap_handover_failure_t fail = {
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
|
||||
};
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
rrc_remove_ue(rrc, ue_context_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1302,9 +1315,8 @@ void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg)
|
||||
*/
|
||||
int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance)
|
||||
{
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[0];
|
||||
uint32_t gNB_ue_ngap_id = 0;
|
||||
gNB_ue_ngap_id = NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id;
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[instance];
|
||||
uint32_t gNB_ue_ngap_id = NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id;
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], gNB_ue_ngap_id);
|
||||
|
||||
if (ue_context_p == NULL) {
|
||||
@@ -1445,20 +1457,24 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U
|
||||
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n");
|
||||
nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid);
|
||||
if (du == NULL) {
|
||||
LOG_E(NR_RRC, "Failed to send Handover Notify: no DU found with NR Cell ID=%lu\n", rrc->nr_cellid);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY);
|
||||
ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p);
|
||||
memset(ho_notify, 0, sizeof(*ho_notify));
|
||||
nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid);
|
||||
|
||||
ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id;
|
||||
ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id;
|
||||
ho_notify->user_info.nrCellIdentity = rrc->nr_cellid;
|
||||
ho_notify->user_info.target_ng_ran.tac = *du->setup_req->cell->info.tac;
|
||||
ho_notify->user_info.target_ng_ran.targetgNBId = rrc->node_id;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mcc = du->setup_req->cell->info.plmn.mcc;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mnc = du->setup_req->cell->info.plmn.mnc;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mnc_digit_length = du->setup_req->cell->info.plmn.mnc_digit_length;
|
||||
|
||||
target_ran_node_id_t *target_ng_ran = &ho_notify->user_info.target_ng_ran;
|
||||
target_ng_ran->tac = *du->setup_req->cell->info.tac;
|
||||
target_ng_ran->targetgNBId = rrc->node_id;
|
||||
target_ng_ran->plmn_identity = du->setup_req->cell->info.plmn;
|
||||
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
@@ -1614,10 +1630,7 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
|
||||
{
|
||||
LOG_I(NR_RRC, "Handover Preparation: send Handover Required (target gNB ID=%d, PCI=%d)\n", neighbour->gNB_ID, neighbour->physicalCellId);
|
||||
|
||||
const plmn_id_t plmn = {.mcc = neighbour->plmn.mnc,
|
||||
.mnc = neighbour->plmn.mnc,
|
||||
.mnc_digit_length = neighbour->plmn.mnc_digit_length};
|
||||
|
||||
const plmn_id_t plmn = neighbour->plmn;
|
||||
const target_ran_node_id_t target = {
|
||||
.plmn_identity = plmn,
|
||||
.tac = neighbour->tac,
|
||||
@@ -1712,7 +1725,7 @@ int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t in
|
||||
s->dl_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit");
|
||||
|
||||
// Send to PDCP layer
|
||||
e1_send_bearer_updates(rrc, UE, 0, NULL, s);
|
||||
e1_notify_pdcp_status(rrc, UE, s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -122,6 +122,18 @@ rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_by_rnti_any_du(gNB_RRC_INST *rrc_in
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @brief Fetch UE Context by the unique AMF UE NGAP ID */
|
||||
rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_by_amf_ue_ngap_id(gNB_RRC_INST *rrc_instance_pP, uint64_t amf_ue_ngap_id)
|
||||
{
|
||||
rrc_gNB_ue_context_t *ue_context_p;
|
||||
RB_FOREACH (ue_context_p, rrc_nr_ue_tree_s, &(rrc_instance_pP->rrc_ue_head)) {
|
||||
if (ue_context_p->ue_context.amf_ue_ngap_id == amf_ue_ngap_id) {
|
||||
return ue_context_p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rrc_gNB_free_mem_ue_context(rrc_gNB_ue_context_t *const ue_context_pP)
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ rrc_gNB_ue_context_t* rrc_gNB_allocate_new_ue_context(gNB_RRC_INST* rrc_instance
|
||||
rrc_gNB_ue_context_t* rrc_gNB_get_ue_context(gNB_RRC_INST* rrc_instance_pP, ue_id_t ue);
|
||||
rrc_gNB_ue_context_t* rrc_gNB_get_ue_context_by_rnti(gNB_RRC_INST* rrc_instance_pP, sctp_assoc_t assoc_id, rnti_t rntiP);
|
||||
rrc_gNB_ue_context_t* rrc_gNB_get_ue_context_by_rnti_any_du(gNB_RRC_INST* rrc_instance_pP, rnti_t rntiP);
|
||||
rrc_gNB_ue_context_t* rrc_gNB_get_ue_context_by_amf_ue_ngap_id(gNB_RRC_INST* rrc_instance_pP, uint64_t amf_ue_ngap_id);
|
||||
|
||||
void rrc_gNB_free_mem_ue_context(rrc_gNB_ue_context_t* const ue_context_pP);
|
||||
|
||||
|
||||
@@ -453,10 +453,8 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
|
||||
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) {
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
uint32_t ue_id = UE->rrc_ue_id;
|
||||
if (UE->ho_context != NULL) {
|
||||
if (UE->ho_context != NULL)
|
||||
LOG_W(NR_RRC, "DU disconnected while handover for UE %d active\n", ue_id);
|
||||
nr_rrc_finalize_ho(UE);
|
||||
}
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
|
||||
if (ue_data.du_assoc_id != assoc_id)
|
||||
continue; /* this UE is on another DU */
|
||||
@@ -619,6 +617,8 @@ void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t
|
||||
|
||||
nr_rrc_du_container_t *get_du_for_ue(gNB_RRC_INST *rrc, uint32_t ue_id)
|
||||
{
|
||||
if (!cu_exists_f1_ue_data(ue_id))
|
||||
return NULL;
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
|
||||
return get_du_by_assoc_id(rrc, ue_data.du_assoc_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user