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:
Jaroslava Fiedlerova
2025-10-01 18:08:28 +02:00
18 changed files with 496 additions and 361 deletions
@@ -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)
+51 -73
View File
@@ -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;
}
+15 -2
View File
@@ -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(&currentMC->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)
+1 -1
View File
@@ -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
View File
@@ -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) {
+33 -20
View File
@@ -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;
+12
View File
@@ -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)
//-----------------------------------------------------------------------------
{
+1
View File
@@ -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);
+3 -3
View File
@@ -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);
}