Files
openairinterface5g/openair2/RRC/NR/rrc_gNB_du.c
T

286 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rrc_gNB_du.h"
#include "rrc_gNB_NGAP.h"
#include "common/ran_context.h"
#include "nr_rrc_defs.h"
#include "rrc_gNB_UE_context.h"
#include "openair2/F1AP/f1ap_common.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "executables/softmodem-common.h"
static int du_compare(const nr_rrc_du_container_t *a, const nr_rrc_du_container_t *b)
{
if (a->assoc_id > b->assoc_id)
return 1;
if (a->assoc_id == b->assoc_id)
return 0;
return -1; /* a->assoc_id < b->assoc_id */
}
/* Tree management functions */
RB_GENERATE/*_STATIC*/(rrc_du_tree, nr_rrc_du_container_t, entries, du_compare);
static bool rrc_gNB_plmn_matches(const gNB_RRC_INST *rrc, const f1ap_served_cell_info_t *info)
{
const gNB_RrcConfigurationReq *conf = &rrc->configuration;
return conf->num_plmn == 1 // F1 supports only one
&& conf->mcc[0] == info->plmn.mcc
&& conf->mnc[0] == info->plmn.mnc;
}
void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
{
AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
gNB_RRC_INST *rrc = RC.nrrrc[0];
DevAssert(rrc);
LOG_I(NR_RRC, "Received F1 Setup Request from gNB_DU %lu (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id);
// check:
// - it is one cell
// - PLMN and Cell ID matches
// - no previous DU with the same ID
// else reject
if (req->num_cells_available != 1) {
LOG_E(NR_RRC, "can only handle on DU cell, but gNB_DU %ld has %d\n", req->gNB_DU_id, req->num_cells_available);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseRadioNetwork_gNB_CU_Cell_Capacity_Exceeded};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
return;
}
f1ap_served_cell_info_t *cell_info = &req->cell[0].info;
if (!rrc_gNB_plmn_matches(rrc, cell_info)) {
LOG_E(NR_RRC,
"PLMN mismatch: CU %d%d, DU %d%d\n",
rrc->configuration.mcc[0],
rrc->configuration.mnc[0],
cell_info->plmn.mcc,
cell_info->plmn.mnc);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
return;
}
nr_rrc_du_container_t *it = NULL;
RB_FOREACH(it, rrc_du_tree, &rrc->dus) {
if (it->setup_req->gNB_DU_id == req->gNB_DU_id) {
LOG_E(NR_RRC,
"gNB-DU ID: existing DU %s on assoc_id %d already has ID %ld, rejecting requesting gNB-DU\n",
it->setup_req->gNB_DU_name,
it->assoc_id,
it->setup_req->gNB_DU_id);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
return;
}
// note: we assume that each DU contains only one cell; otherwise, we would
// need to check every cell in the requesting DU to any existing cell.
const f1ap_served_cell_info_t *exist_info = &it->setup_req->cell[0].info;
const f1ap_served_cell_info_t *new_info = &req->cell[0].info;
if (exist_info->nr_cellid == new_info->nr_cellid || exist_info->nr_pci == new_info->nr_pci) {
LOG_E(NR_RRC,
"existing DU %s on assoc_id %d already has cellID %ld/physCellId %d, rejecting requesting gNB-DU with cellID %ld/physCellId %d\n",
it->setup_req->gNB_DU_name,
it->assoc_id,
exist_info->nr_cellid,
exist_info->nr_pci,
new_info->nr_cellid,
new_info->nr_pci);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
return;
}
}
// if there is no system info or no SIB1 and we run in SA mode, we cannot handle it
const f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info;
NR_BCCH_BCH_Message_t *mib = NULL;
NR_SIB1_t *sib1 = NULL;
if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && get_softmodem_params()->sa)) {
asn_dec_rval_t dec_rval =
uper_decode_complete(NULL, &asn_DEF_NR_BCCH_BCH_Message, (void **)&mib, sys_info->mib, sys_info->mib_length);
if (dec_rval.code != RC_OK || mib->message.present != NR_BCCH_BCH_MessageType_PR_mib
|| mib->message.choice.messageClassExtension == NULL) {
LOG_E(RRC, "Failed to decode NR_BCCH_BCH_MESSAGE (%zu bits) of DU, rejecting DU\n", dec_rval.consumed);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseProtocol_semantic_error};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
ASN_STRUCT_FREE(asn_DEF_NR_BCCH_BCH_Message, mib);
return;
}
if (sys_info->sib1) {
dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_SIB1, (void **)&sib1, sys_info->sib1, sys_info->sib1_length);
if (dec_rval.code != RC_OK) {
LOG_E(RRC, "Failed to decode NR_SIB1 (%zu bits) of DU, rejecting DU\n", dec_rval.consumed);
f1ap_setup_failure_t fail = {.cause = F1AP_CauseProtocol_semantic_error};
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, sib1);
return;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_SIB1, sib1);
}
}
LOG_I(RRC, "Accepting DU %ld (%s), sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name);
LOG_I(RRC, "DU uses RRC version %u.%u.%u\n", req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]);
// we accept the DU
nr_rrc_du_container_t *du = calloc(1, sizeof(*du));
AssertFatal(du, "out of memory\n");
du->assoc_id = assoc_id;
/* ITTI will free the setup request message via free(). So the memory
* "inside" of the message will remain, but the "outside" container no, so
* allocate memory and copy it in */
du->setup_req = calloc(1,sizeof(*du->setup_req));
AssertFatal(du->setup_req, "out of memory\n");
*du->setup_req = *req;
if (mib != NULL && sib1 != NULL) {
du->mib = mib->message.choice.mib;
mib->message.choice.mib = NULL;
ASN_STRUCT_FREE(asn_DEF_NR_BCCH_BCH_MessageType, mib);
du->sib1 = sib1;
}
RB_INSERT(rrc_du_tree, &rrc->dus, du);
rrc->num_dus++;
served_cells_to_activate_t cell = {
.plmn = cell_info->plmn,
.nr_cellid = cell_info->nr_cellid,
.nrpci = cell_info->nr_pci,
.num_SI = 0,
};
f1ap_setup_resp_t resp = {.transaction_id = req->transaction_id,
.num_cells_to_activate = 1,
.cells_to_activate[0] = cell};
int num = read_version(TO_STRING(NR_RRC_VERSION), &resp.rrc_ver[0], &resp.rrc_ver[1], &resp.rrc_ver[2]);
AssertFatal(num == 3, "could not read RRC version string %s\n", TO_STRING(NR_RRC_VERSION));
if (rrc->node_name != NULL)
resp.gNB_CU_name = strdup(rrc->node_name);
rrc->mac_rrc.f1_setup_response(assoc_id, &resp);
/*
MessageDef *msg_p2 = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_GNB_CU_CONFIGURATION_UPDATE);
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).gNB_CU_name = rrc->node_name;
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].plmn.mcc = rrc->configuration.mcc[0];
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].plmn.mnc = rrc->configuration.mnc[0];
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0];
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].nr_cellid = rrc->nr_cellid;
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].nrpci = req->cell[0].info.nr_pci;
int num_SI = 0;
if (rrc->carrier.SIB23) {
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].SI_container[2] = rrc->carrier.SIB23;
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].SI_container_length[2] = rrc->carrier.sizeof_SIB23;
num_SI++;
}
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).cells_to_activate[0].num_SI = num_SI;
F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p2).num_cells_to_activate = 1;
// send
itti_send_msg_to_task(TASK_CU_F1, 0, msg_p2);
*/
}
static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
{
int count = 0;
rrc_gNB_ue_context_t *ue_context_p = NULL;
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) {
uint32_t ue_id = ue_context_p->ue_context.rrc_ue_id;
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
if (ue_data.du_assoc_id == assoc_id) {
/* this UE belongs to the DU that disconnected, set du_assoc_id to 0,
* meaning DU is offline, then trigger release request */
cu_remove_f1_ue_data(ue_id);
ue_data.du_assoc_id = 0;
cu_add_f1_ue_data(ue_id, &ue_data);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0,
ue_context_p,
NGAP_CAUSE_RADIO_NETWORK,
NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST);
count++;
}
}
return count;
}
void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id)
{
AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
(void) lc; // unused for the moment
nr_rrc_du_container_t e = {.assoc_id = assoc_id};
nr_rrc_du_container_t *du = RB_FIND(rrc_du_tree, &rrc->dus, &e);
if (du == NULL) {
LOG_W(NR_RRC, "no DU connected or not found for assoc_id %d: F1 Setup Failed?\n", assoc_id);
return;
}
f1ap_setup_req_t *req = du->setup_req;
LOG_I(RRC, "releasing DU ID %ld (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id);
ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib);
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1);
/* TODO: free setup request */
nr_rrc_du_container_t *removed = RB_REMOVE(rrc_du_tree, &rrc->dus, du);
DevAssert(removed != NULL);
rrc->num_dus--;
int num = invalidate_du_connections(rrc, assoc_id);
if (num > 0) {
LOG_I(NR_RRC, "%d UEs lost through DU disconnect\n", num);
}
}
nr_rrc_du_container_t *get_du_for_ue(gNB_RRC_INST *rrc, uint32_t ue_id)
{
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);
}
nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
{
nr_rrc_du_container_t e = {.assoc_id = assoc_id};
return RB_FIND(rrc_du_tree, &rrc->dus, &e);
}
void dump_du_info(const gNB_RRC_INST *rrc, FILE *f)
{
fprintf(f, "%ld connected DUs \n", rrc->num_dus);
int i = 1;
nr_rrc_du_container_t *du = NULL;
/* cast is necessary to eliminate warning "discards ‘const’ qualifier" */
RB_FOREACH(du, rrc_du_tree, &((gNB_RRC_INST *)rrc)->dus) {
const f1ap_setup_req_t *sr = du->setup_req;
fprintf(f, "[%d] DU ID %ld (%s) ", i++, sr->gNB_DU_id, sr->gNB_DU_name);
if (du->assoc_id == -1) {
fprintf(f, "integrated DU-CU");
} else {
fprintf(f, "assoc_id %d", du->assoc_id);
}
const f1ap_served_cell_info_t *info = &sr->cell[0].info;
fprintf(f, ": nrCellID %ld, PCI %d\n", info->nr_cellid, info->nr_pci);
}
}