Commit 6fd02ee7 authored by Guido Casati's avatar Guido Casati

Handle N2 handover in UE Context Setup Response on the target NG-RAN

* generate Handover Command for the source NG-RAN
  to be added to the target to source transparent
  container of the Handover Acknowledge message
* add Handover Acknowledge callback for N2
* send NG Handover Acknowledge to AMF
Co-authored-by: default avatarbatuhan duyuler <batuhan.duyuler@firecell.io>
parent fa75484e
......@@ -82,6 +82,7 @@
#include "NR_UE-CapabilityRequestFilterNR.h"
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverPreparationInformation-IEs.h"
#include "NR_HandoverCommand.h"
#include "common/utils/nr/nr_common.h"
#if defined(NR_Rel16)
#include "NR_SCS-SpecificCarrier.h"
......@@ -1138,6 +1139,113 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
return mc;
}
/** @brief Prepares removal of measurement configurations related to the source gNB.
* This function decodes the HandoverPreparationInformation received from the source gNB,
* extracts the source MeasConfig, and marks those entries (measurement objects, report
* configurations, and measurement IDs) for removal by populating the corresponding
* ToRemoveList fields in the UE's current measConfig.
* This ensures that obsolete or source-specific measurement configurations are removed
* from the UE after handover and allows the target gNB to install its own valid configuration.
* @param measConfig Target gNB's current measurement configuration to be updated.
* @param prep_info Encoded HandoverPreparationInformation received from the source gNB.*/
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_array_t prep_info)
{
if (currentMC == NULL) {
LOG_E(NR_RRC, "HO LOG: UE's Measurement Configuration is NULL!\n");
return;
}
// Decodes the HandoverPreparationInformation message
NR_HandoverPreparationInformation_t *hpi = NULL;
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_HandoverPreparationInformation,
(void **)&hpi,
(uint8_t *)prep_info.buf,
prep_info.len);
if (hoPrep_dec_rval.code != RC_OK || hoPrep_dec_rval.consumed < 0) {
LOG_E(NR_RRC, "Handover Prep Info decode error while removing source gnb measurement configuration!\n");
return;
}
/* Decodes the RRCReconfiguration message within the HandoverPreparationInformation
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");
return;
}
NR_RRCReconfiguration_t *rrcReconf = NULL;
NR_AS_Config_t *sourceConfig = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig;
asn_dec_rval_t rrcReconf_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_RRCReconfiguration,
(void **)&rrcReconf,
(uint8_t *)sourceConfig->rrcReconfiguration.buf,
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");
return;
}
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL)
return;
NR_MeasConfig_t *sourceMC = rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig;
/* Add measurement objects, report configurations, and measurement IDs
to the UE's measConfig removal lists, and update UE->measConfig */
if (sourceMC->measObjectToAddModList->list.count > 0) {
currentMC->measObjectToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measObjectToRemoveList));
for (int i = 0; i < sourceMC->measObjectToAddModList->list.count; i++) {
NR_MeasObjectId_t *measObjId = calloc_or_fail(1, sizeof(NR_MeasObjectId_t));
*measObjId = sourceMC->measObjectToAddModList->list.array[i]->measObjectId;
asn1cSeqAdd(&currentMC->measObjectToRemoveList->list, measObjId);
}
}
if (sourceMC->reportConfigToAddModList->list.count > 0) {
currentMC->reportConfigToRemoveList = calloc_or_fail(1, sizeof(*currentMC->reportConfigToRemoveList));
for (int i = 0; i < sourceMC->reportConfigToAddModList->list.count; i++) {
NR_ReportConfigId_t *reportConfigId = calloc_or_fail(1, sizeof(NR_ReportConfigId_t));
*reportConfigId = sourceMC->reportConfigToAddModList->list.array[i]->reportConfigId;
asn1cSeqAdd(&currentMC->reportConfigToRemoveList->list, reportConfigId);
}
}
if (sourceMC->measIdToAddModList->list.count > 0) {
currentMC->measIdToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measIdToRemoveList));
for (int i = 0; i < sourceMC->measIdToAddModList->list.count; i++) {
NR_MeasId_t *measId = calloc_or_fail(1, sizeof(NR_MeasId_t));
*measId = sourceMC->measIdToAddModList->list.array[i]->measId;
asn1cSeqAdd(&currentMC->measIdToRemoveList->list, measId);
}
}
}
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params)
{
// Encode RRCReconfiguration to buffer
byte_array_t msg = do_HO_RRCReconfiguration(params);
// Add RRCReconfiguration to handoverCommand
NR_HandoverCommand_t HoCommand = {0};
HoCommand.criticalExtensions.present = NR_HandoverCommand__criticalExtensions_PR_c1;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1, c1);
c1->present = NR_HandoverCommand__criticalExtensions__c1_PR_handoverCommand;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1->choice.handoverCommand, hc);
OCTET_STRING_fromBuf(&hc->handoverCommandMessage, (const char *)msg.buf, msg.len);
free_byte_array(msg);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverCommand, (void *)&HoCommand);
// Encode handoverCommand to buffer
byte_array_t buffer = {.len = -1, .buf = NULL};
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverCommand, NULL, (void *)&HoCommand, (void **)&buffer.buf);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverCommand, &HoCommand);
return buffer;
}
/** @brief HandoverPreparationInformation message (11.2.2 of TS 38.331)
* 1) generate RRCReconfiguration to be transferred
* 2) encodes UE Capabilities from UE Context
......
......@@ -163,5 +163,7 @@ void free_MeasConfig(NR_MeasConfig_t *mc);
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci);
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
......@@ -408,6 +408,73 @@ static byte_array_t rrc_gNB_generate_HandoverPreparationInformation(gNB_RRC_INST
return hoPrepInfo;
}
static byte_array_t rrc_gNB_encode_HandoverCommand(gNB_RRC_UE_t *UE, gNB_RRC_INST *rrc)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->target);
NR_SecurityConfig_t *sec = calloc_or_fail(1, sizeof(*sec));
sec->keyToUse = calloc_or_fail(1, sizeof(*sec->keyToUse));
*sec->keyToUse = NR_SecurityConfig__keyToUse_master;
sec->securityAlgorithmConfig = calloc_or_fail(1, sizeof(*sec->securityAlgorithmConfig));
struct NR_SecurityAlgorithmConfig *alg = sec->securityAlgorithmConfig;
alg->cipheringAlgorithm = UE->ciphering_algorithm;
alg->integrityProtAlgorithm = calloc_or_fail(1, sizeof(*alg->integrityProtAlgorithm));
*alg->integrityProtAlgorithm = UE->integrity_algorithm;
// Mark source gNB's measurement configuration for removal in UE->measConfig
if (UE->ho_context->target->ue_ho_prep_info.len) {
fill_removal_lists_from_source_measConfig(UE->measConfig, UE->ho_context->target->ue_ho_prep_info);
}
/* 3GPP TS 38.331 RadioBearerConfig: re-establish PDCP whenever
the security key used for this radio bearer changes, e.g. for SRB2
when receiving reconfiguration with sync [...]. Similarly, for DRBs
PDCP re-establishment is necessary with derivation of new UP keys */
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, UE, (1 << 1) | (1 << 2), true);
UE->xids[params.transaction_id] = RRC_DEDICATED_RECONF;
params.security_config = sec;
params.masterKeyUpdate = true;
params.nextHopChainingCount = UE->nh_ncc;
byte_array_t out = get_HandoverCommandMessage(&params);
// Free remove lists in UE->measConfig
ASN_STRUCT_FREE(asn_DEF_NR_MeasIdToRemoveList, UE->measConfig->measIdToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_ReportConfigToRemoveList, UE->measConfig->reportConfigToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_MeasObjectToRemoveList, UE->measConfig->measObjectToRemoveList);
free_RRCReconfiguration_params(params);
return out;
}
/** @brief This callback is used by the target gNB
* to trigger the Handover Request Acknowledge towards the AMF */
static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
AssertFatal(cu_exists_f1_ue_data(UE->rrc_ue_id), "No CU found for rrc_ue_id %d\n", UE->rrc_ue_id);
f1_ue_data_t previous_data = cu_get_f1_ue_data(UE->rrc_ue_id);
/* this is the callback for N2 handover after F1 UE context setup response in
* the handover case. Check that there was no associated DU, then set it */
AssertFatal(previous_data.secondary_ue == -1, "there was already a DU present\n");
previous_data.secondary_ue = UE->ho_context->target->du_ue_id;
bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &previous_data);
DevAssert(success);
LOG_I(NR_RRC, "Updated CU F1AP Context for UE %d, DU Id : %u\n", UE->rrc_ue_id, UE->ho_context->target->du_ue_id);
byte_array_t hoCommand = rrc_gNB_encode_HandoverCommand(UE, rrc);
if (hoCommand.len < 0) {
LOG_E(NR_RRC, "ASN1 message encoding failed: failed to generate Handover Command Message\n");
return;
} else {
LOG_D(NR_RRC, "HO LOG: Handover Command for UE %u Encoded (%ld bytes)\n", UE->rrc_ue_id, hoCommand.len);
}
rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(rrc, UE, hoCommand);
free_byte_array(hoCommand);
}
/** @brief Callback function to trigger NG Handover Failure on the target gNB, to inform the AMF
* that the preparation of resources has failed (e.g. unsatisfied criteria, gNB is already loaded).
* This message represents an Unsuccessful Outcome of the Handover Resource Allocation */
......
......@@ -53,6 +53,7 @@ typedef struct nr_ho_source_cu {
/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
typedef struct nr_ho_target_cu {
/// pointer to the (target) DU structure
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment