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

3136 lines
132 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
*/
/*! \file rrc_gNB.c
* \brief rrc procedures for gNB
* \author Navid Nikaein and Raymond Knopp , WEI-TAI CHEN
* \date 2011 - 2014 , 2018
* \version 1.0
* \company Eurecom, NTUST
* \email: navid.nikaein@eurecom.fr and raymond.knopp@eurecom.fr, kroempa@gmail.com
*/
#define RRC_GNB_C
#define RRC_GNB_C
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "nr_rrc_config.h"
#include "nr_rrc_defs.h"
#include "nr_rrc_extern.h"
#include "assertions.h"
#include "common/ran_context.h"
#include "oai_asn1.h"
#include "rrc_gNB_radio_bearers.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
#include "common/utils/LOG/log.h"
#include "RRC/NR/MESSAGES/asn1_msg.h"
#include "openair2/E1AP/e1ap_asnc.h"
#include "NR_BCCH-BCH-Message.h"
#include "NR_UL-DCCH-Message.h"
#include "NR_DL-DCCH-Message.h"
#include "NR_DL-CCCH-Message.h"
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCReject.h"
#include "NR_RejectWaitTime.h"
#include "NR_RRCSetup.h"
#include "NR_CellGroupConfig.h"
#include "NR_MeasResults.h"
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCSetupRequest-IEs.h"
#include "NR_RRCSetupComplete-IEs.h"
#include "NR_RRCReestablishmentRequest-IEs.h"
#include "NR_MIB.h"
#include "uper_encoder.h"
#include "uper_decoder.h"
#include "platform_types.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "T.h"
#include "RRC/NAS/nas_config.h"
#include "RRC/NAS/rb_config.h"
#include "openair3/SECU/secu_defs.h"
#include "rrc_gNB_NGAP.h"
#include "rrc_gNB_GTPV1U.h"
#include "nr_pdcp/nr_pdcp_entity.h"
#include "nr_pdcp/nr_pdcp_oai_api.h"
#include "intertask_interface.h"
#include "SIMULATION/TOOLS/sim.h" // for taus
#include "executables/softmodem-common.h"
#include <openair2/RRC/NR/rrc_gNB_UE_context.h>
#include <openair2/X2AP/x2ap_eNB.h>
#include <openair3/SECU/key_nas_deriver.h>
#include <openair3/ocp-gtpu/gtp_itf.h>
#include <openair2/RRC/NR/nr_rrc_proto.h>
#include "openair2/F1AP/f1ap_common.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
#include "cucp_cuup_if.h"
#include "BIT_STRING.h"
#include "assertions.h"
//#define XER_PRINT
extern RAN_CONTEXT_t RC;
static inline uint64_t bitStr_to_uint64(BIT_STRING_t *asn);
mui_t rrc_gNB_mui = 0;
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///
NR_DRB_ToAddModList_t *fill_DRB_configList(gNB_RRC_UE_t *ue)
{
gNB_RRC_INST *rrc = RC.nrrrc[0];
if (ue->nb_of_pdusessions == 0)
return NULL;
int nb_drb_to_setup = rrc->configuration.drbs;
long drb_priority[MAX_DRBS_PER_UE] = {0};
uint8_t drb_id_to_setup_start = 0;
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
for (int i = 0; i < ue->nb_of_pdusessions; i++) {
if (ue->pduSession[i].status >= PDU_SESSION_STATUS_DONE) {
continue;
}
LOG_I(NR_RRC, "adding rnti %x pdusession %d, nb drb %d\n", ue->rnti, ue->pduSession[i].param.pdusession_id, nb_drb_to_setup);
for (long drb_id_add = 1; drb_id_add <= nb_drb_to_setup; drb_id_add++) {
uint8_t drb_id;
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
for (int qos_flow_index = 0; qos_flow_index < ue->pduSession[i].param.nb_qos; qos_flow_index++) {
switch (ue->pduSession[i].param.qos[qos_flow_index].fiveQI) {
case 1 ... 4: /* GBR */
drb_id = next_available_drb(ue, &ue->pduSession[i], GBR_FLOW);
break;
case 5 ... 9: /* Non-GBR */
if (rrc->configuration.drbs > 1) { /* Force the creation from gNB Conf file */
LOG_W(NR_RRC, "Adding %d DRBs, from gNB config file (not decided by 5GC\n", rrc->configuration.drbs);
drb_id = next_available_drb(ue, &ue->pduSession[i], GBR_FLOW);
} else {
drb_id = next_available_drb(ue, &ue->pduSession[i], NONGBR_FLOW);
}
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", ue->pduSession[i].param.qos[qos_flow_index].fiveQI);
ue->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
continue;
}
drb_priority[drb_id - 1] = ue->pduSession[i].param.qos[qos_flow_index].allocation_retention_priority.priority_level;
if (drb_priority[drb_id - 1] < 0 || drb_priority[drb_id - 1] > NGAP_PRIORITY_LEVEL_NO_PRIORITY) {
LOG_E(NR_RRC, "invalid allocation_retention_priority.priority_level %ld set to _NO_PRIORITY\n", drb_priority[drb_id - 1]);
drb_priority[drb_id - 1] = NGAP_PRIORITY_LEVEL_NO_PRIORITY;
}
if (drb_is_active(ue, drb_id)) { /* Non-GBR flow using the same DRB or a GBR flow with no available DRBs*/
nb_drb_to_setup--;
} else {
generateDRB(ue,
drb_id,
&ue->pduSession[i],
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering);
NR_DRB_ToAddMod_t *DRB_config = generateDRB_ASN1(&ue->established_drbs[drb_id - 1]);
if (drb_id_to_setup_start == 0)
drb_id_to_setup_start = DRB_config->drb_Identity;
asn1cSeqAdd(&DRB_configList->list, DRB_config);
}
LOG_D(RRC, "DRB Priority %ld\n", drb_priority[drb_id]); // To supress warning for now
}
}
}
if (DRB_configList->list.count == 0) {
free(DRB_configList);
return NULL;
}
return DRB_configList;
}
static void freeDRBlist(NR_DRB_ToAddModList_t *list)
{
//ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, list);
return;
}
typedef struct deliver_dl_rrc_message_data_s {
const gNB_RRC_INST *rrc;
f1ap_dl_rrc_message_t *dl_rrc;
} deliver_dl_rrc_message_data_t;
static void rrc_deliver_dl_rrc_message(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
deliver_dl_rrc_message_data_t *data = (deliver_dl_rrc_message_data_t *)deliver_pdu_data;
data->dl_rrc->rrc_container = (uint8_t *)buf;
data->dl_rrc->rrc_container_length = size;
DevAssert(data->dl_rrc->srb_id == srb_id);
data->rrc->mac_rrc.dl_rrc_message_transfer(data->dl_rrc);
}
void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue_p, uint8_t srb_id, const uint8_t* buffer, int size)
{
DevAssert(size > 0);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, .srb_id = srb_id};
deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc};
nr_pdcp_data_req_srb(ue_p->rrc_ue_id, srb_id, rrc_gNB_mui++, size, (unsigned char *const)buffer, rrc_deliver_dl_rrc_message, &data);
}
static int get_dl_band(const f1ap_served_cell_info_t *cell_info)
{
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.dl_freqinfo.band;
}
static int get_ul_band(const f1ap_served_cell_info_t *cell_info)
{
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.ul_freqinfo.band;
}
static int get_ssb_scs(const f1ap_served_cell_info_t *cell_info)
{
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs;
}
static int get_dl_arfcn(const f1ap_served_cell_info_t *cell_info)
{
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.arfcn : cell_info->fdd.dl_freqinfo.arfcn;
}
static int get_dl_bw(const f1ap_served_cell_info_t *cell_info)
{
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.nrb : cell_info->fdd.dl_tbw.nrb;
}
static int get_ssb_arfcn(const f1ap_served_cell_info_t *cell_info, const NR_MIB_t *mib, const NR_SIB1_t *sib1)
{
DevAssert(cell_info != NULL && sib1 != NULL && mib != NULL);
const NR_FrequencyInfoDL_SIB_t* freq_info = &sib1->servingCellConfigCommon->downlinkConfigCommon.frequencyInfoDL;
AssertFatal(freq_info->scs_SpecificCarrierList.list.count == 1, "cannot handle more than one carrier, but has %d\n", freq_info->scs_SpecificCarrierList.list.count);
AssertFatal(freq_info->scs_SpecificCarrierList.list.array[0]->offsetToCarrier == 0, "cannot handle offsetToCarrier != 0, but is %ld\n", freq_info->scs_SpecificCarrierList.list.array[0]->offsetToCarrier);
long offsetToPointA = freq_info->offsetToPointA;
long kssb = mib->ssb_SubcarrierOffset;
uint32_t dl_arfcn = get_dl_arfcn(cell_info);
int scs = get_ssb_scs(cell_info);
int band = get_dl_band(cell_info);
uint64_t scaling = band < 100 ? 1 : 3;
uint64_t freqpointa = from_nrarfcn(band, scs, dl_arfcn);
// offsetToPointA and kSSB are both on 15kHz SCS (see 38.211 sections 7.4.3.1
// and 4.4.4.2)
// SSB uses the SCS of the cell and is 20 RBs wide, so use 10
uint64_t freqssb = freqpointa + scaling * 15000 * (offsetToPointA * 12 + kssb) + 10ll * 12 * (1 << scs) * 15000;
int bw_index = get_supported_band_index(scs, band, get_dl_bw(cell_info));
int band_size_hz = get_supported_bw_mhz(band > 256 ? FR2 : FR1, bw_index) * 1000 * 1000;
uint32_t ssb_arfcn = to_nrarfcn(band, freqssb, scs, band_size_hz);
LOG_W(RRC, "freqpointa %ld Hz/%d offsetToPointA %ld kssb %ld scs %d band %d band_size_hz %d freqssb %ld Hz/%d\n", freqpointa, dl_arfcn, offsetToPointA, kssb, scs, band, band_size_hz, freqssb, ssb_arfcn);
if (RC.nrmac) {
// debugging: let's test this is the correct ARFCN
// in the CU, we have no access to the SSB ARFCN and therefore need to
// compute it ourselves. If we are running in monolithic, though, we have
// access to the MAC structures and hence can read and compare to the
// original SSB ARFCN. If the below creates problems, it can safely be
// taken out (but the reestablishment will likely not work).
const NR_ServingCellConfigCommon_t *scc = RC.nrmac[0]->common_channels[0].ServingCellConfigCommon;
uint32_t scc_ssb_arfcn = *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB;
AssertFatal(ssb_arfcn == scc_ssb_arfcn, "mismatch of SCC SSB ARFCN original %d vs. computed %d! Note: you can remove this Assert, the gNB might run but UE connection instable\n", scc_ssb_arfcn, ssb_arfcn);
}
return ssb_arfcn;
}
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///
static void init_NR_SI(gNB_RRC_INST *rrc)
{
if (!NODE_IS_DU(rrc->node_type)) {
rrc->carrier.SIB23 = (uint8_t *) malloc16(100);
AssertFatal(rrc->carrier.SIB23 != NULL, "cannot allocate memory for SIB");
rrc->carrier.sizeof_SIB23 = do_SIB23_NR(&rrc->carrier);
LOG_I(NR_RRC,"do_SIB23_NR, size %d \n ", rrc->carrier.sizeof_SIB23);
AssertFatal(rrc->carrier.sizeof_SIB23 != 255,"FATAL, RC.nrrrc[mod].carrier[CC_id].sizeof_SIB23 == 255");
}
if (get_softmodem_params()->phy_test > 0 || get_softmodem_params()->do_ra > 0) {
AssertFatal(NODE_IS_MONOLITHIC(rrc->node_type), "phy_test and do_ra only work in monolithic\n");
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_allocate_new_ue_context(rrc);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
UE->spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig));
UE->spCellConfig->spCellConfigDedicated = RC.nrmac[0]->common_channels[0].pre_ServingCellConfig;
LOG_I(NR_RRC,"Adding new user (%p)\n",ue_context_p);
if (!NODE_IS_CU(RC.nrrrc[0]->node_type)) {
rrc_add_nsa_user(rrc,ue_context_p,NULL);
}
}
}
static void rrc_gNB_CU_DU_init(gNB_RRC_INST *rrc)
{
switch (rrc->node_type) {
case ngran_gNB_CUCP:
mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
cucp_cuup_message_transfer_e1ap_init(rrc);
break;
case ngran_gNB_CU:
mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
cucp_cuup_message_transfer_direct_init(rrc);
break;
case ngran_gNB:
mac_rrc_dl_direct_init(&rrc->mac_rrc);
cucp_cuup_message_transfer_direct_init(rrc);
break;
case ngran_gNB_DU:
/* silently drop this, as we currently still need the RRC at the DU. As
* soon as this is not the case anymore, we can add the AssertFatal() */
//AssertFatal(1==0,"nothing to do for DU\n");
break;
default:
AssertFatal(0 == 1, "Unknown node type %d\n", rrc->node_type);
break;
}
cu_init_f1_ue_data();
}
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration)
{
AssertFatal(rrc != NULL, "RC.nrrrc not initialized!");
AssertFatal(NUMBER_OF_UE_MAX < (module_id_t)0xFFFFFFFFFFFFFFFF, " variable overflow");
AssertFatal(configuration!=NULL,"configuration input is null\n");
rrc->module_id = 0;
rrc_gNB_CU_DU_init(rrc);
uid_linear_allocator_init(&rrc->uid_allocator);
RB_INIT(&rrc->rrc_ue_head);
RB_INIT(&rrc->cuups);
rrc->configuration = *configuration;
/// System Information INIT
init_NR_SI(rrc);
return;
} // END openair_rrc_gNB_configuration
static void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_addition_req_t *m)
{
struct NR_CG_ConfigInfo *cg_configinfo = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_CG_ConfigInfo,
(void **)&cg_configinfo,
(uint8_t *)m->rrc_buffer,
(int) m->rrc_buffer_size);//m->rrc_buffer_size);
gNB_RRC_INST *rrc=RC.nrrrc[gnb_mod_idP];
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
AssertFatal(1==0,"NR_UL_DCCH_MESSAGE decode error\n");
// free the memory
SEQUENCE_free(&asn_DEF_NR_CG_ConfigInfo, cg_configinfo, 1);
return;
}
xer_fprint(stdout,&asn_DEF_NR_CG_ConfigInfo, cg_configinfo);
// recreate enough of X2 EN-DC Container
AssertFatal(cg_configinfo->criticalExtensions.choice.c1->present == NR_CG_ConfigInfo__criticalExtensions__c1_PR_cg_ConfigInfo,
"ueCapabilityInformation not present\n");
parse_CG_ConfigInfo(rrc,cg_configinfo,m);
LOG_A(NR_RRC, "Successfully parsed CG_ConfigInfo of size %zu bits. (%zu bytes)\n",
dec_rval.consumed, (dec_rval.consumed +7/8));
}
//-----------------------------------------------------------------------------
unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
//-----------------------------------------------------------------------------
{
static unsigned int transaction_id[NUMBER_OF_gNB_MAX] = {0};
// used also in NGAP thread, so need thread safe operation
unsigned int tmp = __atomic_add_fetch(&transaction_id[gnb_mod_idP], 1, __ATOMIC_SEQ_CST);
tmp %= NR_RRC_TRANSACTION_IDENTIFIER_NUMBER;
LOG_T(NR_RRC, "generated xid is %d\n", tmp);
return tmp;
}
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
if (!ue->Srb[1].Active) {
LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
return NULL;
}
NR_SRB_ToAddModList_t *list = CALLOC(sizeof(*list), 1);
for (int i = 0; i < maxSRBs; i++)
if (ue->Srb[i].Active) {
asn1cSequenceAdd(list->list, NR_SRB_ToAddMod_t, srb);
srb->srb_Identity = i;
if (reestablish && i == 2) {
asn1cCallocOne(srb->reestablishPDCP, NR_SRB_ToAddMod__reestablishPDCP_true);
}
}
return list;
}
static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
NR_DRB_ToAddMod_t *DRB_config = NULL;
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
if (ue->established_drbs[i].status != DRB_INACTIVE) {
DRB_config = generateDRB_ASN1(&ue->established_drbs[i]);
if (reestablish) {
ue->established_drbs[i].reestablishPDCP = NR_DRB_ToAddMod__reestablishPDCP_true;
asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
}
asn1cSeqAdd(&DRB_configList->list, DRB_config);
}
}
return DRB_configList;
}
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
{
if (l) {
for (int i = 0; i < l->list.count; i++)
free(l->list.array[i]);
free(l);
} else
LOG_E(NR_RRC, "Call free SRB list on NULL pointer\n");
}
//-----------------------------------------------------------------------------
static void rrc_gNB_generate_RRCSetup(instance_t instance,
rnti_t rnti,
rrc_gNB_ue_context_t *const ue_context_pP,
const uint8_t *masterCellGroup,
int masterCellGroup_len)
//-----------------------------------------------------------------------------
{
LOG_I(NR_RRC, "rrc_gNB_generate_RRCSetup for RNTI %04x\n", rnti);
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
gNB_RRC_INST *rrc = RC.nrrrc[instance];
unsigned char buf[1024];
uint8_t xid = rrc_gNB_get_next_transaction_identifier(instance);
ue_p->xids[xid] = RRC_SETUP;
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
int size = do_RRCSetup(ue_context_pP, buf, xid, masterCellGroup, masterCellGroup_len, &rrc->configuration, SRBs);
AssertFatal(size > 0, "do_RRCSetup failed\n");
AssertFatal(size <= 1024, "memory corruption\n");
LOG_DUMPMSG(NR_RRC, DEBUG_RRC,
(char *)buf,
size,
"[MSG] RRC Setup\n");
nr_pdcp_add_srbs(true, ue_p->rrc_ue_id, SRBs, 0, NULL, NULL);
freeSRBlist(SRBs);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
f1ap_dl_rrc_message_t dl_rrc = {
.gNB_CU_ue_id = ue_p->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.rrc_container = buf,
.rrc_container_length = size,
.srb_id = CCCH
};
rrc->mac_rrc.dl_rrc_message_transfer(&dl_rrc);
}
static void rrc_gNB_generate_RRCReject(module_id_t module_id, rrc_gNB_ue_context_t *const ue_context_pP)
//-----------------------------------------------------------------------------
{
LOG_I(NR_RRC, "rrc_gNB_generate_RRCReject \n");
gNB_RRC_INST *rrc = RC.nrrrc[module_id];
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
unsigned char buf[1024];
int size = do_RRCReject(module_id, buf);
AssertFatal(size > 0, "do_RRCReject failed\n");
AssertFatal(size <= 1024, "memory corruption\n");
LOG_DUMPMSG(NR_RRC, DEBUG_RRC,
(char *)buf,
size,
"[MSG] RRCReject \n");
LOG_I(NR_RRC, " [RAPROC] ue %04x Logical Channel DL-CCCH, Generating NR_RRCReject (bytes %d)\n", ue_p->rnti, size);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
f1ap_dl_rrc_message_t dl_rrc = {
.gNB_CU_ue_id = ue_p->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.rrc_container = buf,
.rrc_container_length = size,
.srb_id = CCCH,
.execute_duplication = 1,
.RAT_frequency_priority_information.en_dc = 0
};
rrc->mac_rrc.dl_rrc_message_transfer(&dl_rrc);
}
//-----------------------------------------------------------------------------
/*
* Process the rrc setup complete message from UE (SRB1 Active)
*/
static void rrc_gNB_process_RRCSetupComplete(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP, NR_RRCSetupComplete_IEs_t *rrcSetupComplete)
//-----------------------------------------------------------------------------
{
LOG_A(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel UL-DCCH, " "processing NR_RRCSetupComplete from UE (SRB1 Active)\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
ue_context_pP->ue_context.Srb[1].Active = 1;
ue_context_pP->ue_context.Srb[2].Active = 0;
ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED;
AssertFatal(ctxt_pP->rntiMaybeUEid == ue_context_pP->ue_context.rrc_ue_id, "logic bug: inconsistent IDs, must use CU UE ID!\n");
if (get_softmodem_params()->sa) {
rrc_gNB_send_NGAP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcSetupComplete);
} else {
rrc_gNB_generate_SecurityModeCommand(ctxt_pP, ue_context_pP);
}
}
//-----------------------------------------------------------------------------
static void rrc_gNB_generate_defaultRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP)
//-----------------------------------------------------------------------------
{
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
AssertFatal(ue_p->nb_of_pdusessions == 0, "logic bug: PDU sessions present before RRC Connection established\n");
uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
ue_p->xids[xid] = RRC_DEFAULT_RECONF;
const nr_rrc_du_container_t *du = rrc->du;
DevAssert(du != NULL);
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));
/* Add all NAS PDUs to the list */
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
OCTET_STRING_fromBuf(msg, (char *)ue_p->pduSession[i].param.nas_pdu.buffer, ue_p->pduSession[i].param.nas_pdu.length);
}
ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
LOG_D(NR_RRC, "setting the status for the default DRB (index %d) to (%d,%s)\n", i, ue_p->pduSession[i].status, "PDU_SESSION_STATUS_DONE");
}
if (ue_p->nas_pdu.length) {
asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
OCTET_STRING_fromBuf(msg, (char *)ue_p->nas_pdu.buffer, ue_p->nas_pdu.length);
}
/* If list is empty free the list and reset the address */
if (dedicatedNAS_MessageList->list.count == 0) {
free(dedicatedNAS_MessageList);
dedicatedNAS_MessageList = NULL;
}
AssertFatal(du->setup_req->num_cells_available == 1, "cannot handle more than one cell, but have %d\n", du->setup_req->num_cells_available);
f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
int scs = get_ssb_scs(cell_info);
int band = get_dl_band(cell_info);
uint32_t ssb_arfcn = get_ssb_arfcn(cell_info, du->mib, du->sib1);
NR_MeasConfig_t *measconfig = get_defaultMeasConfig(ssb_arfcn, band, scs);
uint8_t buffer[RRC_BUF_SIZE] = {0};
int size = do_RRCReconfiguration(ue_p,
buffer,
RRC_BUF_SIZE,
xid,
NULL, //*SRB_configList2,
NULL, //*DRB_configList,
NULL,
NULL,
measconfig,
dedicatedNAS_MessageList,
ue_p->masterCellGroup);
AssertFatal(size > 0, "cannot encode RRCReconfiguration in %s()\n", __func__);
free_defaultMeasConfig(measconfig);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, ue_p->masterCellGroup);
}
// suspicious if it is always malloced before ?
free(ue_p->nas_pdu.buffer);
LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n");
/* Free all NAS PDUs */
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
free(ue_p->pduSession[i].param.nas_pdu.buffer);
ue_p->pduSession[i].param.nas_pdu.buffer = NULL;
}
}
LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate NR_RRCReconfiguration (bytes %d, UE id %x)\n",
ctxt_pP->module_id,
ctxt_pP->frame,
size,
ue_context_pP->ue_context.rnti);
AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
}
//-----------------------------------------------------------------------------
void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP)
//-----------------------------------------------------------------------------
{
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
int drb_id_to_setup_start = 1;
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
NR_DRB_ToAddModList_t *DRB_configList = fill_DRB_configList(ue_p);
int nb_drb_to_setup = DRB_configList ? DRB_configList->list.count : 0;
ue_p->xids[xid] = RRC_PDUSESSION_ESTABLISH;
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL;
NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL;
dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList));
for (int i=0; i < nb_drb_to_setup; i++) {
NR_DRB_ToAddMod_t *DRB_config = DRB_configList->list.array[i];
if (drb_id_to_setup_start == 1)
drb_id_to_setup_start = DRB_config->drb_Identity;
int j = ue_p->nb_of_pdusessions - 1;
AssertFatal(j >= 0, "");
if (ue_p->pduSession[j].param.nas_pdu.buffer != NULL) {
dedicatedNAS_Message = CALLOC(1, sizeof(NR_DedicatedNAS_Message_t));
memset(dedicatedNAS_Message, 0, sizeof(OCTET_STRING_t));
OCTET_STRING_fromBuf(dedicatedNAS_Message,
(char *)ue_p->pduSession[j].param.nas_pdu.buffer,
ue_p->pduSession[j].param.nas_pdu.length);
ue_p->pduSession[j].status = PDU_SESSION_STATUS_DONE;
asn1cSeqAdd(&dedicatedNAS_MessageList->list, dedicatedNAS_Message);
LOG_I(NR_RRC, "add NAS info with size %d (pdusession idx %d)\n", ue_p->pduSession[j].param.nas_pdu.length, j);
} else {
// TODO
LOG_E(NR_RRC, "no NAS info (pdusession idx %d)\n", j);
}
ue_p->pduSession[j].xid = xid;
}
freeDRBlist(DRB_configList);
/* If list is empty free the list and reset the address */
if (dedicatedNAS_MessageList->list.count == 0) {
free(dedicatedNAS_MessageList);
dedicatedNAS_MessageList = NULL;
}
/* Free all NAS PDUs */
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
/* Free the NAS PDU buffer and invalidate it */
free(ue_p->pduSession[i].param.nas_pdu.buffer);
ue_p->pduSession[i].param.nas_pdu.buffer = NULL;
}
}
NR_CellGroupConfig_t *cellGroupConfig = ue_p->masterCellGroup;
uint8_t buffer[RRC_BUF_SIZE] = {0};
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
int size = do_RRCReconfiguration(ue_p,
buffer,
RRC_BUF_SIZE,
xid,
SRBs,
DRBs,
ue_p->DRB_ReleaseList,
NULL,
NULL,
dedicatedNAS_MessageList,
cellGroupConfig);
LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n");
freeSRBlist(SRBs);
freeDRBlist(DRBs);
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, ue_p->DRB_ReleaseList);
ue_p->DRB_ReleaseList = NULL;
LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate RRCReconfiguration (bytes %d, UE RNTI %x)\n", ctxt_pP->module_id, ctxt_pP->frame, size, ue_p->rnti);
LOG_D(NR_RRC,
"[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
ctxt_pP->frame,
ctxt_pP->module_id,
size,
ue_p->rnti,
rrc_gNB_mui,
ctxt_pP->module_id,
DCCH);
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
}
//-----------------------------------------------------------------------------
void
rrc_gNB_modify_dedicatedRRCReconfiguration(
const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_pP)
//-----------------------------------------------------------------------------
{
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
NR_DRB_ToAddModList_t *DRB_configList = fill_DRB_configList(ue_p);
int qos_flow_index = 0;
uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
ue_p->xids[xid] = RRC_PDUSESSION_MODIFY;
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList =
CALLOC(1, sizeof(*dedicatedNAS_MessageList));
NR_DRB_ToAddMod_t *DRB_config = NULL;
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
// bypass the new and already configured pdu sessions
if (ue_p->pduSession[i].status >= PDU_SESSION_STATUS_DONE) {
ue_p->pduSession[i].xid = xid;
continue;
}
if (ue_p->pduSession[i].cause != NGAP_CAUSE_NOTHING) {
// set xid of failure pdu session
ue_p->pduSession[i].xid = xid;
ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
continue;
}
// search exist DRB_config
int j;
for (j = 0; i < MAX_DRBS_PER_UE; j++) {
if (ue_p->established_drbs[j].status != DRB_INACTIVE
&& ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == ue_p->pduSession[i].param.pdusession_id)
break;
}
if (j == MAX_DRBS_PER_UE) {
ue_p->pduSession[i].xid = xid;
ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_unspecified;
continue;
}
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
for (qos_flow_index = 0; qos_flow_index < ue_p->pduSession[i].param.nb_qos; qos_flow_index++) {
switch (ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI) {
case 1: //100ms
case 2: //150ms
case 3: //50ms
case 4: //300ms
case 5: //100ms
case 6: //300ms
case 7: //100ms
case 8: //300ms
case 9: //300ms Video (Buffered Streaming)TCP-based (e.g., www, e-mail, chat, ftp, p2p file sharing, progressive video, etc.)
// TODO
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
ue_p->pduSession[i].xid = xid;
ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_not_supported_5QI_value;
continue;
}
LOG_I(NR_RRC,
"PDU SESSION ID %ld, DRB ID %ld (index %d), QOS flow %d, 5QI %ld \n",
DRB_config->cnAssociation->choice.sdap_Config->pdu_Session,
DRB_config->drb_Identity,
i,
qos_flow_index,
ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
}
asn1cSeqAdd(&DRB_configList->list, DRB_config);
ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
ue_p->pduSession[i].xid = xid;
if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
asn1cSequenceAdd(dedicatedNAS_MessageList->list,NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)ue_p->pduSession[i].param.nas_pdu.buffer, ue_p->pduSession[i].param.nas_pdu.length);
LOG_I(NR_RRC, "add NAS info with size %d (pdusession id %d)\n", ue_p->pduSession[i].param.nas_pdu.length, ue_p->pduSession[i].param.pdusession_id);
} else {
LOG_W(NR_RRC, "no NAS info (pdusession id %d)\n", ue_p->pduSession[i].param.pdusession_id);
}
}
/* If list is empty free the list and reset the address */
if (dedicatedNAS_MessageList->list.count == 0) {
free(dedicatedNAS_MessageList);
dedicatedNAS_MessageList = NULL;
}
uint8_t buffer[RRC_BUF_SIZE];
int size = do_RRCReconfiguration(ue_p,
buffer,
RRC_BUF_SIZE,
xid,
NULL,
DRB_configList,
NULL,
NULL,
NULL,
dedicatedNAS_MessageList,
NULL);
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
/* Free all NAS PDUs */
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
/* Free the NAS PDU buffer and invalidate it */
free(ue_p->pduSession[i].param.nas_pdu.buffer);
ue_p->pduSession[i].param.nas_pdu.buffer = NULL;
}
}
LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate RRCReconfiguration (bytes %d, UE RNTI %x)\n", ctxt_pP->module_id, ctxt_pP->frame, size, ue_p->rnti);
LOG_D(NR_RRC,
"[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
ctxt_pP->frame,
ctxt_pP->module_id,
size,
ue_p->rnti,
rrc_gNB_mui,
ctxt_pP->module_id,
DCCH);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
}
//-----------------------------------------------------------------------------
void
rrc_gNB_generate_dedicatedRRCReconfiguration_release(
const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *const ue_context_pP,
uint8_t xid,
uint32_t nas_length,
uint8_t *nas_buffer)
//-----------------------------------------------------------------------------
{
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
NR_DRB_ToReleaseList_t *DRB_Release_configList2 = CALLOC(sizeof(*DRB_Release_configList2), 1);
for (int i = 0; i < NB_RB_MAX; i++) {
if ((ue_p->pduSession[i].status == PDU_SESSION_STATUS_TORELEASE) && ue_p->pduSession[i].xid == xid) {
asn1cSequenceAdd(DRB_Release_configList2->list, NR_DRB_Identity_t, DRB_release);
*DRB_release = i + 1;
}
}
/* If list is empty free the list and reset the address */
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL;
if (nas_length > 0) {
dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));
asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)nas_buffer, nas_length);
LOG_I(NR_RRC,"add NAS info with size %d\n", nas_length);
} else {
LOG_W(NR_RRC,"dedlicated NAS list is empty\n");
}
uint8_t buffer[RRC_BUF_SIZE] = {0};
int size = do_RRCReconfiguration(ue_p,
buffer,
RRC_BUF_SIZE,
xid,
NULL,
NULL,
DRB_Release_configList2,
NULL,
NULL,
dedicatedNAS_MessageList,
NULL);
LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n");
/* Free all NAS PDUs */
if (nas_length > 0) {
/* Free the NAS PDU buffer and invalidate it */
free(nas_buffer);
}
LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate NR_RRCReconfiguration (bytes %d, UE RNTI %x)\n", ctxt_pP->module_id, ctxt_pP->frame, size, ue_p->rnti);
LOG_D(NR_RRC,
"[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
ctxt_pP->frame,
ctxt_pP->module_id,
size,
ue_p->rnti,
rrc_gNB_mui,
ctxt_pP->module_id,
DCCH);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
}
//-----------------------------------------------------------------------------
/*
* Process the RRC Reconfiguration Complete from the UE
*/
static void rrc_gNB_process_RRCReconfigurationComplete(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP, const uint8_t xid)
{
int drb_id;
uint8_t kRRCenc[16] = {0};
uint8_t kRRCint[16] = {0};
uint8_t kUPenc[16] = {0};
uint8_t kUPint[16] = {0};
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
NR_DRB_ToAddModList_t *DRB_configList = createDRBlist(ue_p, false);
/* Derive the keys from kgnb */
if (DRB_configList != NULL) {
nr_derive_key(UP_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kUPenc);
nr_derive_key(UP_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, kUPint);
}
nr_derive_key(RRC_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kRRCenc);
nr_derive_key(RRC_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, kRRCint);
/* Refresh SRBs/DRBs */
LOG_D(NR_RRC, "Configuring PDCP DRBs/SRBs for UE %04x\n", ue_p->rnti);
ue_context_pP->ue_context.ue_reconfiguration_after_reestablishment_counter++;
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
nr_pdcp_add_srbs(ctxt_pP->enb_flag,
ue_p->rrc_ue_id,
SRBs,
(ue_p->integrity_algorithm << 4) | ue_p->ciphering_algorithm,
kRRCenc,
kRRCint);
freeSRBlist(SRBs);
nr_pdcp_add_drbs(ctxt_pP->enb_flag,
ue_p->rrc_ue_id,
DRB_configList,
(ue_p->integrity_algorithm << 4) | ue_p->ciphering_algorithm,
kUPenc,
kUPint);
/* Loop through DRBs and establish if necessary */
if (DRB_configList != NULL) {
for (int i = 0; i < DRB_configList->list.count; i++) {
if (DRB_configList->list.array[i]) {
drb_id = (int)DRB_configList->list.array[i]->drb_Identity;
LOG_A(NR_RRC,
"[gNB %d] Frame %d : Logical Channel UL-DCCH, Received NR_RRCReconfigurationComplete from UE rnti %lx, reconfiguring DRB %d\n",
ctxt_pP->module_id,
ctxt_pP->frame,
ctxt_pP->rntiMaybeUEid,
(int)DRB_configList->list.array[i]->drb_Identity);
//(int)*DRB_configList->list.array[i]->pdcp_Config->moreThanOneRLC->primaryPath.logicalChannel);
if (ue_p->DRB_active[drb_id - 1] == 0) {
ue_p->DRB_active[drb_id - 1] = DRB_ACTIVE;
LOG_D(NR_RRC, "[gNB %d] Frame %d: Establish RLC UM Bidirectional, DRB %d Active\n",
ctxt_pP->module_id, ctxt_pP->frame, (int)DRB_configList->list.array[i]->drb_Identity);
LOG_D(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT" RRC_gNB --- MAC_CONFIG_REQ (DRB) ---> MAC_gNB\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
//if (DRB_configList->list.array[i]->pdcp_Config->moreThanOneRLC->primaryPath.logicalChannel) {
// nr_DRB2LCHAN[i] = (uint8_t) * DRB_configList->list.array[i]->pdcp_Config->moreThanOneRLC->primaryPath.logicalChannel;
//}
// rrc_mac_config_req_eNB
} else { // remove LCHAN from MAC/PHY
if (ue_p->DRB_active[drb_id] == 1) {
/* TODO : It may be needed if gNB goes into full stack working. */
// DRB has just been removed so remove RLC + PDCP for DRB
/* rrc_pdcp_config_req (ctxt_pP->module_id, frameP, 1, CONFIG_ACTION_REMOVE,
(ue_mod_idP * NB_RB_MAX) + DRB2LCHAN[i],UNDEF_SECURITY_MODE);
*/
/*rrc_rlc_config_req(ctxt_pP,
SRB_FLAG_NO,
MBMS_FLAG_NO,
CONFIG_ACTION_REMOVE,
nr_DRB2LCHAN[i]);*/
}
// ue_p->DRB_active[drb_id] = 0;
LOG_D(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" RRC_eNB --- MAC_CONFIG_REQ (DRB) ---> MAC_eNB\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
// rrc_mac_config_req_eNB
} // end else of if (ue_p->DRB_active[drb_id] == 0)
} // end if (DRB_configList->list.array[i])
} // end for (int i = 0; i < DRB_configList->list.count; i++)
} // end if DRB_configList != NULL
freeDRBlist(DRB_configList);
}
//-----------------------------------------------------------------------------
static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context_pP,
const uint8_t *masterCellGroup_from_DU,
const rnti_t old_rnti,
const nr_rrc_du_container_t *du)
//-----------------------------------------------------------------------------
{
module_id_t module_id = 0;
gNB_RRC_INST *rrc = RC.nrrrc[module_id];
int enable_ciphering = 0;
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
uint8_t buffer[RRC_BUF_SIZE] = {0};
uint8_t xid = rrc_gNB_get_next_transaction_identifier(module_id);
ue_p->xids[xid] = RRC_REESTABLISH;
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, true);
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
uint32_t ssb_arfcn = get_ssb_arfcn(cell_info, du->mib, du->sib1);
int size = do_RRCReestablishment(ue_context_pP, buffer, RRC_BUF_SIZE, xid, cell_info->nr_pci, ssb_arfcn);
LOG_I(NR_RRC, "[RAPROC] UE %04x Logical Channel DL-DCCH, Generating NR_RRCReestablishment (bytes %d)\n", ue_p->rnti, size);
uint8_t kRRCenc[16] = {0};
uint8_t kRRCint[16] = {0};
uint8_t kUPenc[16] = {0};
/* Derive the keys from kgnb */
if (ue_p->Srb[1].Active)
nr_derive_key(UP_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kUPenc);
nr_derive_key(RRC_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kRRCenc);
nr_derive_key(RRC_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, kRRCint);
freeSRBlist(SRBs);
LOG_I(NR_RRC, "Set PDCP security UE %d RNTI %04x nca %ld nia %d in RRCReestablishment\n", ue_p->rrc_ue_id, ue_p->rnti, ue_p->ciphering_algorithm, ue_p->integrity_algorithm);
uint8_t security_mode =
enable_ciphering ? ue_p->ciphering_algorithm | (ue_p->integrity_algorithm << 4) : 0 | (ue_p->integrity_algorithm << 4);
for (int srb_id = 1; srb_id < maxSRBs; srb_id++) {
if (ue_p->Srb[srb_id].Active) {
nr_pdcp_config_set_security(ue_p->rrc_ue_id, srb_id, security_mode, kRRCenc, kRRCint, kUPenc);
}
}
// TODO: should send E1 UE Bearer Modification with PDCP Reestablishment flag
nr_pdcp_reestablishment(ue_p->rrc_ue_id);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
uint32_t old_gNB_DU_ue_id = old_rnti;
f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.srb_id = DCCH,
.old_gNB_DU_ue_id = &old_gNB_DU_ue_id};
deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc};
nr_pdcp_data_req_srb(ue_p->rrc_ue_id, DCCH, rrc_gNB_mui++, size, (unsigned char *const)buffer, rrc_deliver_dl_rrc_message, &data);
}
/// @brief Function tha processes RRCReestablishmentComplete message sent by the UE, after RRCReestasblishment request.
/// @param ctxt_pP Protocol context containing information regarding the UE and gNB
/// @param reestablish_rnti is the old C-RNTI
/// @param ue_context_pP UE context container information regarding the UE
/// @param xid Transaction Identifier used in RRC messages
static void rrc_gNB_process_RRCReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_pP,
const uint8_t xid)
{
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
LOG_I(NR_RRC,
"[RAPROC] UE %04x Logical Channel UL-DCCH, processing NR_RRCReestablishmentComplete from UE (SRB1 Active)\n",
ue_p->rnti);
int i = 0;
uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
ue_p->StatusRrc = NR_RRC_CONNECTED;
ue_p->Srb[1].Active = 1;
if (get_softmodem_params()->sa) {
uint8_t send_security_mode_command = false;
nr_rrc_pdcp_config_security(ctxt_pP, ue_context_pP, send_security_mode_command);
LOG_D(NR_RRC, "RRC Reestablishment - set security successfully \n");
}
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
NR_CellGroupConfig_t *cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t));
cellGroupConfig->spCellConfig = ue_p->masterCellGroup->spCellConfig;
cellGroupConfig->mac_CellGroupConfig = ue_p->masterCellGroup->mac_CellGroupConfig;
cellGroupConfig->physicalCellGroupConfig = ue_p->masterCellGroup->physicalCellGroupConfig;
cellGroupConfig->rlc_BearerToReleaseList = NULL;
cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList));
/*
* Get SRB2, DRB configuration from the existing UE context,
* also start from SRB2 (i=1) and not from SRB1 (i=0).
*/
for (i = 1; i < ue_p->masterCellGroup->rlc_BearerToAddModList->list.count; ++i)
asn1cSeqAdd(&cellGroupConfig->rlc_BearerToAddModList->list, ue_p->masterCellGroup->rlc_BearerToAddModList->list.array[i]);
for (i = 0; i < cellGroupConfig->rlc_BearerToAddModList->list.count; i++) {
asn1cCallocOne(cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC,
NR_RLC_BearerConfig__reestablishRLC_true);
}
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, true);
NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, true);
uint8_t buffer[RRC_BUF_SIZE] = {0};
int size = do_RRCReconfiguration(ue_p,
buffer,
RRC_BUF_SIZE,
new_xid,
SRBs,
DRBs,
NULL,
NULL,
NULL, // MeasObj_list,
NULL,
cellGroupConfig);
freeSRBlist(SRBs);
freeDRBlist(DRBs);
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
AssertFatal(size > 0, "cannot encode RRC Reconfiguration\n");
LOG_I(NR_RRC,
"UE %d RNTI %04x: Generate NR_RRCReconfiguration after reestablishment complete (bytes %d)\n",
ue_p->rrc_ue_id,
ue_p->rnti,
size);
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
}
//-----------------------------------------------------------------------------
int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP,
protocol_ctxt_t *const ctxt_pP,
const int dl_bwp_id,
const int ul_bwp_id) {
uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
ue_p->xids[xid] = RRC_DEDICATED_RECONF;
NR_CellGroupConfig_t *masterCellGroup = ue_p->masterCellGroup;
if (dl_bwp_id > 0) {
*masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = dl_bwp_id;
*masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = dl_bwp_id;
}
if (ul_bwp_id > 0) {
*masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id;
}
uint8_t buffer[RRC_BUF_SIZE];
int size = do_RRCReconfiguration(ue_p, buffer, RRC_BUF_SIZE, xid, NULL, NULL, NULL, NULL, NULL, NULL, masterCellGroup);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
return 0;
}
/*------------------------------------------------------------------------------*/
static int nr_rrc_gNB_decode_ccch(module_id_t module_id, const f1ap_initial_ul_rrc_message_t *msg)
{
asn_dec_rval_t dec_rval;
NR_UL_CCCH_Message_t *ul_ccch_msg = NULL;
gNB_RRC_INST *gnb_rrc_inst = RC.nrrrc[module_id];
NR_RRCSetupRequest_IEs_t *rrcSetupRequest = NULL;
NR_RRCReestablishmentRequest_IEs_t rrcReestablishmentRequest;
rnti_t rnti = msg->crnti;
/* look up corresponding DU. For the moment, there is only one */
const nr_rrc_du_container_t *du = gnb_rrc_inst->du;
AssertFatal(du != NULL, "received CCCH message, but no corresponding DU found\n");
LOG_I(NR_RRC, "Decoding CCCH: RNTI %04x, payload_size %d\n", rnti, msg->rrc_container_length);
dec_rval =
uper_decode(NULL, &asn_DEF_NR_UL_CCCH_Message, (void **)&ul_ccch_msg, msg->rrc_container, msg->rrc_container_length, 0, 0);
if (dec_rval.code != RC_OK || dec_rval.consumed == 0) {
LOG_E(NR_RRC, " FATAL Error in receiving CCCH\n");
return -1;
}
if (ul_ccch_msg->message.present == NR_UL_CCCH_MessageType_PR_c1) {
switch (ul_ccch_msg->message.choice.c1->present) {
case NR_UL_CCCH_MessageType__c1_PR_NOTHING:
/* TODO */
LOG_I(NR_RRC, "Received PR_NOTHING on UL-CCCH-Message\n");
break;
case NR_UL_CCCH_MessageType__c1_PR_rrcSetupRequest:
LOG_D(NR_RRC, "Received RRCSetupRequest on UL-CCCH-Message (UE rnti %04x)\n", rnti);
{
rrcSetupRequest = &ul_ccch_msg->message.choice.c1->choice.rrcSetupRequest->rrcSetupRequest;
rrc_gNB_ue_context_t *ue_context_p = NULL;
if (NR_InitialUE_Identity_PR_randomValue == rrcSetupRequest->ue_Identity.present) {
/* randomValue BIT STRING (SIZE (39)) */
if (rrcSetupRequest->ue_Identity.choice.randomValue.size != 5) { // 39-bit random value
LOG_E(NR_RRC, "wrong InitialUE-Identity randomValue size, expected 5, provided %lu", (long unsigned int)rrcSetupRequest->ue_Identity.choice.randomValue.size);
ASN_STRUCT_FREE(asn_DEF_NR_UL_CCCH_Message, ul_ccch_msg);
return -1;
}
uint64_t random_value = 0;
memcpy(((uint8_t *)&random_value) + 3, rrcSetupRequest->ue_Identity.choice.randomValue.buf, rrcSetupRequest->ue_Identity.choice.randomValue.size);
/* if there is already a registered UE (with another RNTI) with this random_value,
* the current one must be removed from MAC/PHY (zombie UE)
*/
if ((ue_context_p = rrc_gNB_ue_context_random_exist(gnb_rrc_inst, random_value))) {
LOG_W(NR_RRC, "new UE rnti (coming with random value) is already there, removing UE %x from MAC/PHY\n", rnti);
AssertFatal(false, "not implemented\n");
}
ue_context_p = rrc_gNB_create_ue_context(rnti, gnb_rrc_inst, random_value, msg->gNB_DU_ue_id);
} else if (NR_InitialUE_Identity_PR_ng_5G_S_TMSI_Part1 == rrcSetupRequest->ue_Identity.present) {
/* TODO */
/* <5G-S-TMSI> = <AMF Set ID><AMF Pointer><5G-TMSI> 48-bit */
/* ng-5G-S-TMSI-Part1 BIT STRING (SIZE (39)) */
if (rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size != 5) {
LOG_E(NR_RRC, "wrong ng_5G_S_TMSI_Part1 size, expected 5, provided %lu \n", (long unsigned int)rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size);
ASN_STRUCT_FREE(asn_DEF_NR_UL_CCCH_Message, ul_ccch_msg);
return -1;
}
uint64_t s_tmsi_part1 = bitStr_to_uint64(&rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1);
// memcpy(((uint8_t *) & random_value) + 3,
// rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.buf,
// rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size);
if ((ue_context_p = rrc_gNB_ue_context_5g_s_tmsi_exist(gnb_rrc_inst, s_tmsi_part1))) {
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(NR_RRC, " 5G-S-TMSI-Part1 exists, old rnti %04x => %04x\n", UE->rnti, rnti);
AssertFatal(false, "not implemented\n");
/* replace rnti in the context */
UE->rnti = rnti;
} else {
LOG_I(NR_RRC, "UE %04x 5G-S-TMSI-Part1 doesn't exist, setting ng_5G_S_TMSI_Part1 => %ld\n", rnti, s_tmsi_part1);
ue_context_p = rrc_gNB_create_ue_context(rnti, gnb_rrc_inst, s_tmsi_part1, msg->gNB_DU_ue_id);
AssertFatal(ue_context_p != NULL, "out of memory\n");
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
UE->Initialue_identity_5g_s_TMSI.presence = true;
UE->ng_5G_S_TMSI_Part1 = s_tmsi_part1;
}
} else {
/* TODO */
uint64_t random_value = 0;
memcpy(((uint8_t *)&random_value) + 3, rrcSetupRequest->ue_Identity.choice.randomValue.buf, rrcSetupRequest->ue_Identity.choice.randomValue.size);
ue_context_p = rrc_gNB_create_ue_context(rnti, gnb_rrc_inst, random_value, msg->gNB_DU_ue_id);
LOG_E(NR_RRC, "RRCSetupRequest without random UE identity or S-TMSI not supported, let's reject the UE %04x\n", rnti);
rrc_gNB_generate_RRCReject(module_id, ue_context_p);
break;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
UE = &ue_context_p->ue_context;
UE->establishment_cause = rrcSetupRequest->establishmentCause;
UE->Srb[1].Active = 1;
rrc_gNB_generate_RRCSetup(module_id,
rnti,
ue_context_p,
msg->du2cu_rrc_container,
msg->du2cu_rrc_container_length);
}
break;
case NR_UL_CCCH_MessageType__c1_PR_rrcResumeRequest:
LOG_I(NR_RRC, "receive rrcResumeRequest message \n");
break;
case NR_UL_CCCH_MessageType__c1_PR_rrcReestablishmentRequest: {
LOG_DUMPMSG(NR_RRC,
DEBUG_RRC,
(char *)(msg->rrc_container),
msg->rrc_container_length,
"[MSG] RRC Reestablishment Request\n");
rrcReestablishmentRequest = ul_ccch_msg->message.choice.c1->choice.rrcReestablishmentRequest->rrcReestablishmentRequest;
const NR_ReestablishmentCause_t cause = rrcReestablishmentRequest.reestablishmentCause;
const long physCellId = rrcReestablishmentRequest.ue_Identity.physCellId;
LOG_I(NR_RRC,
"UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n",
rnti,
physCellId,
cause == NR_ReestablishmentCause_otherFailure
? "Other Failure"
: (cause == NR_ReestablishmentCause_handoverFailure ? "Handover Failure" : "reconfigurationFailure"));
AssertFatal(du->setup_req->num_cells_available == 1, "cannot handle more than one cell\n");
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
if (physCellId != cell_info->nr_pci) {
/* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */
LOG_E(NR_RRC,
" NR_RRCReestablishmentRequest ue_Identity.physCellId(%ld) is not equal to current physCellId(%d), fallback to RRC establishment\n",
physCellId,
cell_info->nr_pci);
/* 38.401 8.7: "If the UE accessed from a gNB-DU other than the
* original one, the gNB-CU should trigger the UE Context Setup
* procedure" we did not implement this yet; TBD for Multi-DU */
AssertFatal(false, "not implemented\n");
break;
}
for (int i = 0; i < rrcReestablishmentRequest.ue_Identity.shortMAC_I.size; i++) {
LOG_D(NR_RRC, "rrcReestablishmentRequest.ue_Identity.shortMAC_I.buf[%d] = %x\n", i, rrcReestablishmentRequest.ue_Identity.shortMAC_I.buf[i]);
}
rnti_t old_rnti = rrcReestablishmentRequest.ue_Identity.c_RNTI;
// TODO: we need to check within a specific DU!
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(gnb_rrc_inst, old_rnti);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
/* in case we need to do RRC Setup, give the UE a new random identity */
uint64_t random_value;
fill_random(&random_value, sizeof(random_value));
random_value = random_value & 0x7fffffffff; /* random value is 39 bits */
if (ue_context_p == NULL) {
LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
ue_context_p = rrc_gNB_create_ue_context(rnti, gnb_rrc_inst, random_value, msg->gNB_DU_ue_id);
ue_context_p->ue_context.Srb[1].Active = 1;
rrc_gNB_generate_RRCSetup(module_id, rnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
break;
}
// 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values
if (rrcReestablishmentRequest.ue_Identity.c_RNTI < 0x1 || rrcReestablishmentRequest.ue_Identity.c_RNTI > 0xffef) {
/* c_RNTI range error should not happen */
LOG_E(NR_RRC,
"NR_RRCReestablishmentRequest c_RNTI %04lx range error, fallback to RRC setup\n",
rrcReestablishmentRequest.ue_Identity.c_RNTI);
ue_context_p = rrc_gNB_create_ue_context(rnti, gnb_rrc_inst, random_value, msg->gNB_DU_ue_id);
ue_context_p->ue_context.Srb[1].Active = 1;
rrc_gNB_generate_RRCSetup(module_id, rnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
break;
}
/* TODO: start timer in ITTI and drop UE if it does not come back */
// update with new RNTI, and update secondary UE association
UE->rnti = rnti;
cu_remove_f1_ue_data(UE->rrc_ue_id);
f1_ue_data_t ue_data = {.secondary_ue = rnti};
cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
UE->reestablishment_cause = cause;
LOG_D(NR_RRC, "Accept RRCReestablishmentRequest from UE physCellId %ld cause %ld\n", physCellId, cause);
rrc_gNB_generate_RRCReestablishment(ue_context_p,
msg->du2cu_rrc_container,
old_rnti,
du);
} break;
case NR_UL_CCCH_MessageType__c1_PR_rrcSystemInfoRequest:
LOG_I(NR_RRC, "UE %04x receive rrcSystemInfoRequest message \n", rnti);
/* TODO */
break;
default:
LOG_E(NR_RRC, "UE %04x Unknown message\n", rnti);
break;
}
}
ASN_STRUCT_FREE(asn_DEF_NR_UL_CCCH_Message, ul_ccch_msg);
return 0;
}
/*! \fn uint64_t bitStr_to_uint64(BIT_STRING_t *)
*\brief This function extract at most a 64 bits value from a BIT_STRING_t object, the exact bits number depend on the BIT_STRING_t contents.
*\param[in] pointer to the BIT_STRING_t object.
*\return the extracted value.
*/
static inline uint64_t bitStr_to_uint64(BIT_STRING_t *asn) {
uint64_t result = 0;
int index;
int shift;
DevCheck ((asn->size > 0) && (asn->size <= 8), asn->size, 0, 0);
shift = ((asn->size - 1) * 8) - asn->bits_unused;
for (index = 0; index < (asn->size - 1); index++) {
result |= (uint64_t)asn->buf[index] << shift;
shift -= 8;
}
result |= asn->buf[index] >> asn->bits_unused;
return result;
}
static void rrc_gNB_process_MeasurementReport(rrc_gNB_ue_context_t *ue_context, NR_MeasurementReport_t *measurementReport)
{
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_MeasurementReport, (void *)measurementReport);
DevAssert(measurementReport->criticalExtensions.present == NR_MeasurementReport__criticalExtensions_PR_measurementReport
&& measurementReport->criticalExtensions.choice.measurementReport != NULL);
gNB_RRC_UE_t *ue_ctxt = &ue_context->ue_context;
ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, ue_ctxt->measResults);
ue_ctxt->measResults = NULL;
const NR_MeasId_t id = measurementReport->criticalExtensions.choice.measurementReport->measResults.measId;
AssertFatal(id, "unexpected MeasResult for MeasurementId %ld received\n", id);
asn1cCallocOne(ue_ctxt->measResults, measurementReport->criticalExtensions.choice.measurementReport->measResults);
/* we "keep" the measurement report, so set to 0 */
free(measurementReport->criticalExtensions.choice.measurementReport);
measurementReport->criticalExtensions.choice.measurementReport = NULL;
}
static int handle_rrcReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_p,
const NR_RRCReestablishmentComplete_t *reestablishment_complete)
{
DevAssert(ue_context_p != NULL);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
DevAssert(reestablishment_complete->criticalExtensions.present
== NR_RRCReestablishmentComplete__criticalExtensions_PR_rrcReestablishmentComplete);
rrc_gNB_process_RRCReestablishmentComplete(ctxt_pP,
ue_context_p,
reestablishment_complete->rrc_TransactionIdentifier);
UE->ue_reestablishment_counter++;
return 0;
}
static int handle_ueCapabilityInformation(const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_p,
const NR_UECapabilityInformation_t *ue_cap_info)
{
AssertFatal(ue_context_p != NULL, "Processing %s() for UE %lx, ue_context_p is NULL\n", __func__, ctxt_pP->rntiMaybeUEid);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(NR_RRC, "got UE capabilities for UE %lx\n", ctxt_pP->rntiMaybeUEid);
int eutra_index = -1;
if (ue_cap_info->criticalExtensions.present == NR_UECapabilityInformation__criticalExtensions_PR_ueCapabilityInformation) {
const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList =
ue_cap_info->criticalExtensions.choice.ueCapabilityInformation->ue_CapabilityRAT_ContainerList;
/* Encode UE-CapabilityRAT-ContainerList for sending to the DU */
free(UE->ue_cap_buffer.buf);
UE->ue_cap_buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
NULL,
ue_CapabilityRAT_ContainerList,
(void **)&UE->ue_cap_buffer.buf);
if (UE->ue_cap_buffer.len <= 0) {
LOG_E(RRC, "could not encode UE-CapabilityRAT-ContainerList, abort handling capabilities\n");
return -1;
}
for (int i = 0; i < ue_CapabilityRAT_ContainerList->list.count; i++) {
const NR_UE_CapabilityRAT_Container_t *ue_cap_container = ue_CapabilityRAT_ContainerList->list.array[i];
if (ue_cap_container->rat_Type == NR_RAT_Type_nr) {
if (UE->UE_Capability_nr) {
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = 0;
}
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_UE_NR_Capability,
(void **)&UE->UE_Capability_nr,
ue_cap_container->ue_CapabilityRAT_Container.buf,
ue_cap_container->ue_CapabilityRAT_Container.size,
0,
0);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
}
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT " Failed to decode nr UE capabilities (%zu bytes)\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = 0;
}
UE->UE_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
if (eutra_index != -1) {
LOG_E(NR_RRC, "fatal: more than 1 eutra capability\n");
exit(1);
}
eutra_index = i;
}
if (ue_cap_container->rat_Type == NR_RAT_Type_eutra_nr) {
if (UE->UE_Capability_MRDC) {
ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
UE->UE_Capability_MRDC = 0;
}
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_UE_MRDC_Capability,
(void **)&UE->UE_Capability_MRDC,
ue_cap_container->ue_CapabilityRAT_Container.buf,
ue_cap_container->ue_CapabilityRAT_Container.size,
0,
0);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
}
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC,
PROTOCOL_NR_RRC_CTXT_FMT " Failed to decode nr UE capabilities (%zu bytes)\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
UE->UE_Capability_MRDC = 0;
}
UE->UE_MRDC_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
}
if (ue_cap_container->rat_Type == NR_RAT_Type_eutra) {
// TODO
}
}
if (eutra_index == -1)
return -1;
}
if (get_softmodem_params()->sa) {
rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(ctxt_pP, ue_context_p, ue_cap_info);
}
// we send the UE capabilities request before RRC connection is complete,
// so we cannot have a PDU session yet
AssertFatal(UE->nb_of_pdusessions == 0, "logic bug: received capabilities while PDU session established\n");
// TODO: send UE context modification response with UE capabilities to
// allow DU configure CellGroupConfig
rrc_gNB_generate_defaultRRCReconfiguration(ctxt_pP, ue_context_p);
return 0;
}
static int handle_rrcSetupComplete(const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_p,
const NR_RRCSetupComplete_t *setup_complete)
{
if (!ue_context_p) {
LOG_I(NR_RRC, "Processing NR_RRCSetupComplete UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
return -1;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
NR_RRCSetupComplete_IEs_t *setup_complete_ies = setup_complete->criticalExtensions.choice.rrcSetupComplete;
if (setup_complete_ies->ng_5G_S_TMSI_Value != NULL) {
if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI_Part2) {
if (setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI_Part2.size != 2) {
LOG_E(NR_RRC,
"wrong ng_5G_S_TMSI_Part2 size, expected 2, provided %lu",
(long unsigned int)
setup_complete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI_Part2.size);
return -1;
}
if (UE->ng_5G_S_TMSI_Part1 != 0) {
UE->ng_5G_S_TMSI_Part2 = BIT_STRING_to_uint16(&setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI_Part2);
}
/* TODO */
} else if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI) {
if (setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.size != 6) {
LOG_E(NR_RRC,
"wrong ng_5G_S_TMSI size, expected 6, provided %lu",
(long unsigned int)setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.size);
return -1;
}
uint64_t fiveg_s_TMSI = bitStr_to_uint64(&setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI);
LOG_I(NR_RRC,
"Received rrcSetupComplete, 5g_s_TMSI: 0x%lX, amf_set_id: 0x%lX(%ld), amf_pointer: 0x%lX(%ld), 5g TMSI: 0x%X \n",
fiveg_s_TMSI,
fiveg_s_TMSI >> 38,
fiveg_s_TMSI >> 38,
(fiveg_s_TMSI >> 32) & 0x3F,
(fiveg_s_TMSI >> 32) & 0x3F,
(uint32_t)fiveg_s_TMSI);
if (UE->Initialue_identity_5g_s_TMSI.presence) {
UE->Initialue_identity_5g_s_TMSI.amf_set_id = fiveg_s_TMSI >> 38;
UE->Initialue_identity_5g_s_TMSI.amf_pointer = (fiveg_s_TMSI >> 32) & 0x3F;
UE->Initialue_identity_5g_s_TMSI.fiveg_tmsi = (uint32_t)fiveg_s_TMSI;
}
}
}
rrc_gNB_process_RRCSetupComplete(ctxt_pP, ue_context_p, setup_complete->criticalExtensions.choice.rrcSetupComplete);
LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT " UE State = NR_RRC_CONNECTED \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
return 0;
}
static void handle_rrcReconfigurationComplete(const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *ue_context_p,
const NR_RRCReconfigurationComplete_t *reconfig_complete)
{
LOG_I(NR_RRC, "Receive RRC Reconfiguration Complete message UE %lx\n", ctxt_pP->rntiMaybeUEid);
AssertFatal(ue_context_p != NULL, "Processing %s() for UE %lx, ue_context_p is NULL\n", __func__, ctxt_pP->rntiMaybeUEid);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint8_t xid = reconfig_complete->rrc_TransactionIdentifier;
rrc_gNB_process_RRCReconfigurationComplete(ctxt_pP, ue_context_p, xid);
bool successful_reconfig = true;
if (get_softmodem_params()->sa) {
switch (UE->xids[xid]) {
case RRC_PDUSESSION_RELEASE: {
gtpv1u_gnb_delete_tunnel_req_t req = {0};
gtpv1u_delete_ngu_tunnel(ctxt_pP->instance, &req);
// NGAP_PDUSESSION_RELEASE_RESPONSE
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(ctxt_pP, ue_context_p, xid);
} break;
case RRC_PDUSESSION_ESTABLISH:
if (UE->nb_of_pdusessions > 0)
rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(ctxt_pP, ue_context_p, xid);
break;
case RRC_PDUSESSION_MODIFY:
rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(ctxt_pP, ue_context_p, xid);
break;
case RRC_DEFAULT_RECONF:
rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(ctxt_pP, ue_context_p);
break;
case RRC_REESTABLISH_COMPLETE:
case RRC_DEDICATED_RECONF:
/* do nothing */
break;
default:
LOG_E(RRC, "Received unexpected transaction type %d for xid %d\n", UE->xids[xid], xid);
successful_reconfig = false;
break;
}
}
gNB_RRC_INST *rrc = RC.nrrrc[0];
f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
f1ap_ue_context_modif_req_t ue_context_modif_req = {
.gNB_CU_ue_id = UE->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.plmn.mcc = rrc->configuration.mcc[0],
.plmn.mnc = rrc->configuration.mnc[0],
.plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
.nr_cellid = rrc->nr_cellid,
.servCellId = 0, /* TODO: correct value? */
.ReconfigComplOutcome = successful_reconfig ? RRCreconf_success : RRCreconf_failure,
};
rrc->mac_rrc.ue_context_modification_request(&ue_context_modif_req);
}
//-----------------------------------------------------------------------------
int rrc_gNB_decode_dcch(const protocol_ctxt_t *const ctxt_pP,
const rb_id_t Srb_id,
const uint8_t *const Rx_sdu,
const sdu_size_t sdu_sizeP)
//-----------------------------------------------------------------------------
{
gNB_RRC_INST *gnb_rrc_inst = RC.nrrrc[ctxt_pP->module_id];
/* we look up by CU UE ID! Do NOT change back to RNTI! */
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(gnb_rrc_inst, ctxt_pP->rntiMaybeUEid);
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %lu, aborting transaction\n", ctxt_pP->rntiMaybeUEid);
return -1;
}
if ((Srb_id != 1) && (Srb_id != 2)) {
LOG_E(NR_RRC, "Received message on SRB%ld, should not have ...\n", Srb_id);
} else {
LOG_D(NR_RRC, "Received message on SRB%ld\n", Srb_id);
}
LOG_D(NR_RRC, "Decoding UL-DCCH Message\n");
{
for (int i = 0; i < sdu_sizeP; i++) {
LOG_T(NR_RRC, "%x.", Rx_sdu[i]);
}
LOG_T(NR_RRC, "\n");
}
NR_UL_DCCH_Message_t *ul_dcch_msg = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_UL_DCCH_Message, (void **)&ul_dcch_msg, Rx_sdu, sdu_sizeP, 0, 0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "Failed to decode UL-DCCH (%zu bytes)\n", dec_rval.consumed);
return -1;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
}
if (ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1) {
switch (ul_dcch_msg->message.choice.c1->present) {
case NR_UL_DCCH_MessageType__c1_PR_NOTHING:
LOG_I(NR_RRC, "Received PR_NOTHING on UL-DCCH-Message\n");
break;
case NR_UL_DCCH_MessageType__c1_PR_rrcReconfigurationComplete:
handle_rrcReconfigurationComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcReconfigurationComplete);
break;
case NR_UL_DCCH_MessageType__c1_PR_rrcSetupComplete:
if (handle_rrcSetupComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcSetupComplete) == -1)
return -1;
break;
case NR_UL_DCCH_MessageType__c1_PR_measurementReport:
DevAssert(ul_dcch_msg != NULL
&& ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1
&& ul_dcch_msg->message.choice.c1
&& ul_dcch_msg->message.choice.c1->present == NR_UL_DCCH_MessageType__c1_PR_measurementReport);
rrc_gNB_process_MeasurementReport(ue_context_p, ul_dcch_msg->message.choice.c1->choice.measurementReport);
break;
case NR_UL_DCCH_MessageType__c1_PR_ulInformationTransfer:
LOG_D(NR_RRC, "Recived RRC GNB UL Information Transfer \n");
if (!ue_context_p) {
LOG_W(NR_RRC, "Processing ulInformationTransfer UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
break;
}
LOG_D(NR_RRC, "[MSG] RRC UL Information Transfer \n");
LOG_DUMPMSG(RRC, DEBUG_RRC, (char *)Rx_sdu, sdu_sizeP, "[MSG] RRC UL Information Transfer \n");
if (get_softmodem_params()->sa) {
rrc_gNB_send_NGAP_UPLINK_NAS(ctxt_pP, ue_context_p, ul_dcch_msg);
}
break;
case NR_UL_DCCH_MessageType__c1_PR_securityModeComplete:
// to avoid segmentation fault
if (!ue_context_p) {
LOG_I(NR_RRC, "Processing securityModeComplete UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
break;
}
LOG_I(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT " received securityModeComplete on UL-DCCH %d from UE\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
DCCH);
LOG_D(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT
" RLC RB %02d --- RLC_DATA_IND %d bytes "
"(securityModeComplete) ---> RRC_eNB\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
DCCH,
sdu_sizeP);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
}
/* configure ciphering */
nr_rrc_pdcp_config_security(ctxt_pP, ue_context_p, 1);
rrc_gNB_generate_UECapabilityEnquiry(ctxt_pP, ue_context_p);
break;
case NR_UL_DCCH_MessageType__c1_PR_securityModeFailure:
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)Rx_sdu, sdu_sizeP, "[MSG] NR RRC Security Mode Failure\n");
LOG_W(NR_RRC,
PROTOCOL_RRC_CTXT_UE_FMT
" RLC RB %02d --- RLC_DATA_IND %d bytes "
"(securityModeFailure) ---> RRC_gNB\n",
PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP),
DCCH,
sdu_sizeP);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
}
rrc_gNB_generate_UECapabilityEnquiry(ctxt_pP, ue_context_p);
break;
case NR_UL_DCCH_MessageType__c1_PR_ueCapabilityInformation:
if (handle_ueCapabilityInformation(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.ueCapabilityInformation)
== -1)
return -1;
break;
case NR_UL_DCCH_MessageType__c1_PR_rrcReestablishmentComplete:
if (handle_rrcReestablishmentComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcReestablishmentComplete)
== -1)
return -1;
break;
default:
break;
}
}
ASN_STRUCT_FREE(asn_DEF_NR_UL_DCCH_Message, ul_dcch_msg);
return 0;
}
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
&& rrc->nr_cellid == info->nr_cellid;
}
static void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
{
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 the first DU
// - it is one cell
// - PLMN and Cell ID matches
// else reject
f1ap_setup_failure_t fail = {.cause = F1AP_CauseRadioNetwork_gNB_CU_Cell_Capacity_Exceeded};
if (rrc->du != NULL) {
const f1ap_setup_req_t *other = rrc->du->setup_req;
LOG_E(NR_RRC, "can only handle one DU, but already serving DU %ld (%s)\n", other->gNB_DU_id, other->gNB_DU_name);
rrc->mac_rrc.f1_setup_failure(&fail);
return;
}
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);
rrc->mac_rrc.f1_setup_failure(&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 cellID %ld, DU %d%d cellID %ld\n",
rrc->configuration.mcc[0],
rrc->configuration.mnc[0],
rrc->nr_cellid,
cell_info->plmn.mcc,
cell_info->plmn.mnc,
cell_info->nr_cellid);
rrc->mac_rrc.f1_setup_failure(&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;
if (sys_info == NULL || sys_info->mib == NULL || (sys_info->sib1 == NULL && get_softmodem_params()->sa)) {
LOG_E(NR_RRC, "no system information provided by DU, rejecting\n");
rrc->mac_rrc.f1_setup_failure(&fail);
return;
}
/* do we need the MIB? for the moment, just check it is valid, then drop it */
NR_BCCH_BCH_Message_t *mib = NULL;
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);
rrc->mac_rrc.f1_setup_failure(&fail);
ASN_STRUCT_FREE(asn_DEF_NR_BCCH_BCH_Message, mib);
return;
}
NR_SIB1_t *sib1 = NULL;
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);
rrc->mac_rrc.f1_setup_failure(&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);
// we accept the DU
rrc->du = calloc(1, sizeof(*rrc->du));
AssertFatal(rrc->du != NULL, "out of memory\n");
rrc->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 */
rrc->du->setup_req = malloc(sizeof(*rrc->du->setup_req));
AssertFatal(rrc->du->setup_req != NULL, "out of memory\n");
*rrc->du->setup_req = *req;
rrc->du->mib = mib->message.choice.mib;
mib->message.choice.mib = NULL;
ASN_STRUCT_FREE(asn_DEF_NR_BCCH_BCH_MessageType, mib);
rrc->du->sib1 = sib1;
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 = {.num_cells_to_activate = 1, .cells_to_activate[0] = cell};
if (rrc->node_name != NULL)
resp.gNB_CU_name = strdup(rrc->node_name);
rrc->mac_rrc.f1_setup_response(&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);
*/
}
void rrc_gNB_process_initial_ul_rrc_message(const f1ap_initial_ul_rrc_message_t *ul_rrc)
{
// first get RRC instance (note, no the ITTI instance)
module_id_t i = 0;
for (i=0; i < RC.nb_nr_inst; i++) {
gNB_RRC_INST *rrc = RC.nrrrc[i];
if (rrc->nr_cellid == ul_rrc->nr_cellid)
break;
}
//AssertFatal(i != RC.nb_nr_inst, "Cell_id not found\n");
// TODO REMOVE_DU_RRC in monolithic mode, the MAC does not have the
// nr_cellid. Thus, the above check would fail. For the time being, just put
// a warning, as we handle one DU only anyway
if (i == RC.nb_nr_inst) {
i = 0;
LOG_W(RRC, "initial UL RRC message nr_cellid %ld does not match RRC's %ld\n", ul_rrc->nr_cellid, RC.nrrrc[0]->nr_cellid);
}
nr_rrc_gNB_decode_ccch(i, ul_rrc);
if (ul_rrc->rrc_container)
free(ul_rrc->rrc_container);
if (ul_rrc->du2cu_rrc_container)
free(ul_rrc->du2cu_rrc_container);
}
void rrc_gNB_process_release_request(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_release_request_t *m)
{
gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
rrc_remove_nsa_user(rrc, m->rnti);
}
void rrc_gNB_process_dc_overall_timeout(const module_id_t gnb_mod_idP, x2ap_ENDC_dc_overall_timeout_t *m)
{
gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
rrc_remove_nsa_user(rrc, m->rnti);
}
static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance_t instance)
{
f1ap_ue_context_setup_t *resp = &F1AP_UE_CONTEXT_SETUP_RESP(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
NR_CellGroupConfig_t *cellGroupConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_CellGroupConfig,
(void **)&cellGroupConfig,
(uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
resp->du_to_cu_rrc_information->cellGroupConfig_length);
AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
if (UE->masterCellGroup) {
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
}
UE->masterCellGroup = cellGroupConfig;
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
/* at this point, we don't have to do anything: the UE context setup request
* includes the Security Command, whose response will trigger the following
* messages (UE capability, to be specific) */
}
static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p)
{
const int instance = 0;
f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id);
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id);
return;
}
/* TODO: marshall types correctly */
LOG_I(NR_RRC, "received UE Context Release Request for UE %u, forwarding to AMF\n", req->gNB_CU_ue_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(instance,
ue_context_p,
NGAP_CAUSE_RADIO_NETWORK,
NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST);
}
static void rrc_delete_ue_data(gNB_RRC_UE_t *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);
}
static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
{
const int instance = 0;
f1ap_ue_context_release_complete_t *complete = &F1AP_UE_CONTEXT_RELEASE_COMPLETE(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, complete->gNB_CU_ue_id);
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", complete->gNB_CU_ue_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, UE->rrc_ue_id);
LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti);
rrc_delete_ue_data(UE);
rrc_gNB_remove_ue_context(rrc, ue_context_p);
}
static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, instance_t instance)
{
f1ap_ue_context_modif_resp_t *resp = &F1AP_UE_CONTEXT_MODIFICATION_RESP(msg_p);
protocol_ctxt_t ctxt = {.rntiMaybeUEid = resp->gNB_CU_ue_id, .module_id = instance, .instance = instance, .enb_flag = 1, .eNB_index = instance};
gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
if (resp->drbs_to_be_setup_length > 0) {
e1ap_bearer_setup_req_t req = {0};
// TODO: bug: we receive the DRBs, but we don't associate to a PDU session
// -> we are not sure which PDU session this is, and fill "all"
req.numPDUSessionsMod = UE->nb_of_pdusessions;
req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
req.gNB_cu_up_ue_id = UE->rrc_ue_id;
for (int i = 0; i < req.numPDUSessionsMod; i++) {
req.pduSessionMod[i].sessionId = UE->pduSession[i].param.pdusession_id;
req.pduSessionMod[i].numDRB2Modify = resp->drbs_to_be_setup_length;
for (int j = 0; j < resp->drbs_to_be_setup_length; j++) {
f1ap_drb_to_be_setup_t *drb_f1 = resp->drbs_to_be_setup + j;
DRB_nGRAN_to_setup_t *drb_e1 = req.pduSessionMod[i].DRBnGRanModList + j;
drb_e1->id = drb_f1->drb_id;
drb_e1->numDlUpParam = drb_f1->up_dl_tnl_length;
drb_e1->DlUpParamList[0].tlAddress = drb_f1->up_dl_tnl[0].tl_address;
drb_e1->DlUpParamList[0].teId = drb_f1->up_dl_tnl[0].teid;
}
}
// send the F1 response message up to update F1-U tunnel info
// it seems the rrc transaction id (xid) is not needed here
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}
if (resp->du_to_cu_rrc_information != NULL && resp->du_to_cu_rrc_information->cellGroupConfig != NULL) {
NR_CellGroupConfig_t *cellGroupConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_CellGroupConfig,
(void **)&cellGroupConfig,
(uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
resp->du_to_cu_rrc_information->cellGroupConfig_length);
AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
if (UE->masterCellGroup) {
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
}
UE->masterCellGroup = cellGroupConfig;
rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p);
}
}
static void rrc_CU_process_ue_modification_required(MessageDef *msg_p)
{
f1ap_ue_context_modif_required_t *required = &F1AP_UE_CONTEXT_MODIFICATION_REQUIRED(msg_p);
protocol_ctxt_t ctxt = {.rntiMaybeUEid = required->gNB_CU_ue_id, .module_id = 0, .instance = 0, .enb_flag = 1, .eNB_index = 0};
gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, required->gNB_CU_ue_id);
if (ue_context_p == NULL) {
LOG_E(RRC, "Could not find UE context for CU UE ID %d, cannot handle UE context modification request\n", required->gNB_CU_ue_id);
f1ap_ue_context_modif_refuse_t refuse = {
.gNB_CU_ue_id = required->gNB_CU_ue_id,
.gNB_DU_ue_id = required->gNB_DU_ue_id,
.cause = F1AP_CAUSE_RADIO_NETWORK,
.cause_value = F1AP_CauseRadioNetwork_unknown_or_already_allocated_gnb_cu_ue_f1ap_id,
};
rrc->mac_rrc.ue_context_modification_refuse(&refuse);
return;
}
if (required->du_to_cu_rrc_information && required->du_to_cu_rrc_information->cellGroupConfig) {
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(RRC,
"UE Context Modification Required: new CellGroupConfig for UE ID %d/RNTI %04x, triggering reconfiguration\n",
UE->rrc_ue_id,
UE->rnti);
NR_CellGroupConfig_t *cellGroupConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_CellGroupConfig,
(void **)&cellGroupConfig,
(uint8_t *)required->du_to_cu_rrc_information->cellGroupConfig,
required->du_to_cu_rrc_information->cellGroupConfig_length);
if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
LOG_E(RRC, "Cell group config decode error, refusing reconfiguration\n");
f1ap_ue_context_modif_refuse_t refuse = {
.gNB_CU_ue_id = required->gNB_CU_ue_id,
.gNB_DU_ue_id = required->gNB_DU_ue_id,
.cause = F1AP_CAUSE_PROTOCOL,
.cause_value = F1AP_CauseProtocol_transfer_syntax_error,
};
rrc->mac_rrc.ue_context_modification_refuse(&refuse);
return;
}
if (UE->masterCellGroup) {
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
LOG_I(RRC, "UE %d/RNTI %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rrc_ue_id, UE->rnti);
}
UE->masterCellGroup = cellGroupConfig;
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
/* trigger reconfiguration */
nr_rrc_reconfiguration_req(ue_context_p, &ctxt, 0, 0);
//rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p);
//rrc_gNB_generate_defaultRRCReconfiguration(&ctxt, ue_context_p);
return;
}
LOG_W(RRC,
"nothing to be done after UE Context Modification Required for UE ID %d/RNTI %04x\n",
required->gNB_CU_ue_id,
required->gNB_DU_ue_id);
}
unsigned int mask_flip(unsigned int x) {
return((((x>>8) + (x<<8))&0xffff)>>6);
}
static unsigned int get_dl_bw_mask(const f1ap_served_cell_info_t *cell_info, const NR_UE_NR_Capability_t *cap)
{
int common_band = get_dl_band(cell_info);
int common_scs = get_ssb_scs(cell_info);
for (int i=0;i<cap->rf_Parameters.supportedBandListNR.list.count;i++) {
NR_BandNR_t *bandNRinfo = cap->rf_Parameters.supportedBandListNR.list.array[i];
if (bandNRinfo->bandNR == common_band) {
if (common_band < 257) { // FR1
switch (common_scs) {
case NR_SubcarrierSpacing_kHz15 :
if (bandNRinfo->channelBWs_DL &&
bandNRinfo->channelBWs_DL->choice.fr1 &&
bandNRinfo->channelBWs_DL->choice.fr1->scs_15kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_DL->choice.fr1->scs_15kHz->buf));
break;
case NR_SubcarrierSpacing_kHz30 :
if (bandNRinfo->channelBWs_DL &&
bandNRinfo->channelBWs_DL->choice.fr1 &&
bandNRinfo->channelBWs_DL->choice.fr1->scs_30kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_DL->choice.fr1->scs_30kHz->buf));
break;
case NR_SubcarrierSpacing_kHz60 :
if (bandNRinfo->channelBWs_DL &&
bandNRinfo->channelBWs_DL->choice.fr1 &&
bandNRinfo->channelBWs_DL->choice.fr1->scs_60kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_DL->choice.fr1->scs_60kHz->buf));
break;
}
}
else {
switch (common_scs) {
case NR_SubcarrierSpacing_kHz60 :
if (bandNRinfo->channelBWs_DL &&
bandNRinfo->channelBWs_DL->choice.fr2 &&
bandNRinfo->channelBWs_DL->choice.fr2->scs_60kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_DL->choice.fr2->scs_60kHz->buf));
break;
case NR_SubcarrierSpacing_kHz120 :
if (bandNRinfo->channelBWs_DL &&
bandNRinfo->channelBWs_DL->choice.fr2 &&
bandNRinfo->channelBWs_DL->choice.fr2->scs_120kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_DL->choice.fr2->scs_120kHz->buf));
break;
}
}
}
}
return(0);
}
static unsigned int get_ul_bw_mask(const f1ap_served_cell_info_t *cell_info, const NR_UE_NR_Capability_t *cap)
{
int common_band = get_ul_band(cell_info);
int common_scs = get_ssb_scs(cell_info);
for (int i=0;i<cap->rf_Parameters.supportedBandListNR.list.count;i++) {
NR_BandNR_t *bandNRinfo = cap->rf_Parameters.supportedBandListNR.list.array[i];
if (bandNRinfo->bandNR == common_band) {
if (common_band < 257) { // FR1
switch (common_scs) {
case NR_SubcarrierSpacing_kHz15 :
if (bandNRinfo->channelBWs_UL &&
bandNRinfo->channelBWs_UL->choice.fr1 &&
bandNRinfo->channelBWs_UL->choice.fr1->scs_15kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_UL->choice.fr1->scs_15kHz->buf));
break;
case NR_SubcarrierSpacing_kHz30 :
if (bandNRinfo->channelBWs_UL &&
bandNRinfo->channelBWs_UL->choice.fr1 &&
bandNRinfo->channelBWs_UL->choice.fr1->scs_30kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_UL->choice.fr1->scs_30kHz->buf));
break;
case NR_SubcarrierSpacing_kHz60 :
if (bandNRinfo->channelBWs_UL &&
bandNRinfo->channelBWs_UL->choice.fr1 &&
bandNRinfo->channelBWs_UL->choice.fr1->scs_60kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_UL->choice.fr1->scs_60kHz->buf));
break;
}
}
else {
switch (common_scs) {
case NR_SubcarrierSpacing_kHz60 :
if (bandNRinfo->channelBWs_UL &&
bandNRinfo->channelBWs_UL->choice.fr2 &&
bandNRinfo->channelBWs_UL->choice.fr2->scs_60kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_UL->choice.fr2->scs_60kHz->buf));
break;
case NR_SubcarrierSpacing_kHz120 :
if (bandNRinfo->channelBWs_UL &&
bandNRinfo->channelBWs_UL->choice.fr2 &&
bandNRinfo->channelBWs_UL->choice.fr2->scs_120kHz)
return(mask_flip((unsigned int)*(uint16_t*)bandNRinfo->channelBWs_UL->choice.fr2->scs_120kHz->buf));
break;
}
}
}
}
return(0);
}
static int get_ul_mimo_layersCB(const f1ap_served_cell_info_t *cell_info, const NR_UE_NR_Capability_t *cap)
{
int common_scs = get_ssb_scs(cell_info);
// check featureSet
NR_FeatureSets_t *fs=cap->featureSets;
if (fs) {
// go through UL feature sets and look for one with current SCS
for (int i=0;i<fs->featureSetsUplinkPerCC->list.count;i++) {
if (fs->featureSetsUplinkPerCC->list.array[i]->supportedSubcarrierSpacingUL == common_scs &&
fs->featureSetsUplinkPerCC->list.array[i]->mimo_CB_PUSCH &&
fs->featureSetsUplinkPerCC->list.array[i]->mimo_CB_PUSCH->maxNumberMIMO_LayersCB_PUSCH)
return(1<<*fs->featureSetsUplinkPerCC->list.array[i]->mimo_CB_PUSCH->maxNumberMIMO_LayersCB_PUSCH);
}
}
return(1);
}
static int get_ul_mimo_layers(const f1ap_served_cell_info_t *cell_info, const NR_UE_NR_Capability_t *cap)
{
int common_scs = get_ssb_scs(cell_info);
// check featureSet
NR_FeatureSets_t *fs=cap->featureSets;
if (fs) {
// go through UL feature sets and look for one with current SCS
for (int i=0;i<fs->featureSetsUplinkPerCC->list.count;i++) {
if (fs->featureSetsUplinkPerCC->list.array[i]->supportedSubcarrierSpacingUL == common_scs &&
fs->featureSetsUplinkPerCC->list.array[i]->maxNumberMIMO_LayersNonCB_PUSCH)
return(1<<*fs->featureSetsUplinkPerCC->list.array[i]->maxNumberMIMO_LayersNonCB_PUSCH);
}
}
return(1);
}
static int get_dl_mimo_layers(const f1ap_served_cell_info_t *cell_info, const NR_UE_NR_Capability_t *cap)
{
int common_scs = get_ssb_scs(cell_info);
// check featureSet
NR_FeatureSets_t *fs=cap->featureSets;
if (fs) {
// go through UL feature sets and look for one with current SCS
for (int i=0;i<fs->featureSetsDownlinkPerCC->list.count;i++) {
if (fs->featureSetsUplinkPerCC->list.array[i]->supportedSubcarrierSpacingUL == common_scs &&
fs->featureSetsDownlinkPerCC->list.array[i]->maxNumberMIMO_LayersPDSCH)
return(2<<*fs->featureSetsDownlinkPerCC->list.array[i]->maxNumberMIMO_LayersPDSCH);
}
}
return(1);
}
void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp, instance_t instance)
{
gNB_RRC_INST *rrc = RC.nrrrc[0];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
AssertFatal(ue_context_p != NULL, "did not find UE with CU UE ID %d\n", resp->gNB_cu_cp_ue_id);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
// currently: we don't have "infrastructure" to save the CU-UP UE ID, so we
// assume (and below check) that CU-UP UE ID == CU-CP UE ID
AssertFatal(resp->gNB_cu_cp_ue_id == resp->gNB_cu_up_ue_id,
"cannot handle CU-UP UE ID different from CU-CP UE ID (%d vs %d)\n",
resp->gNB_cu_cp_ue_id,
resp->gNB_cu_up_ue_id);
// save the tunnel address for the PDU sessions
for (int i=0; i < resp->numPDUSessions; i++) {
pdu_session_setup_t *e1_pdu = &resp->pduSession[i];
rrc_pdu_session_param_t *rrc_pdu = find_pduSession(UE, e1_pdu->id, false);
if (rrc_pdu == NULL) {
LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id);
continue;
}
rrc_pdu->param.gNB_teid_N3 = e1_pdu->teId;
memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tlAddress, sizeof(uint8_t) * 4);
rrc_pdu->param.gNB_addr_N3.length = sizeof(in_addr_t);
}
/* Instruction towards the DU for DRB configuration and tunnel creation */
int nb_drb = resp->pduSession[0].numDRBSetup;
f1ap_drb_to_be_setup_t drbs[nb_drb];
rrc_pdu_session_param_t *RRC_pduSession = find_pduSession(UE, e1ap_resp->pduSession[0].id, false);
DevAssert(RRC_pduSession);
for (int i = 0; i < nb_drb; i++) {
drbs[i].drb_id = resp->pduSession[0].DRBnGRanList[i].id;
drbs[i].rlc_mode = rrc->configuration.um_on_default_drb ? RLC_MODE_UM : RLC_MODE_AM;
drbs[i].up_ul_tnl[0].tl_address = resp->pduSession[0].DRBnGRanList[i].UpParamList[0].tlAddress;
drbs[i].up_ul_tnl[0].port = rrc->eth_params_s.my_portd;
drbs[i].up_ul_tnl[0].teid = resp->pduSession[0].DRBnGRanList[i].UpParamList[0].teId;
drbs[i].up_ul_tnl_length = 1;
/* pass NSSAI info to MAC */
drbs[i].nssai = RRC_pduSession->param.nssai;
}
/* Instruction towards the DU for SRB2 configuration */
int nb_srb = 0;
f1ap_srb_to_be_setup_t srbs[1];
if (UE->Srb[2].Active == 0) {
UE->Srb[2].Active = 1;
nb_srb = 1;
srbs[0].srb_id = 2;
srbs[0].lcid = 2;
}
/* Gather UE capability if present */
cu_to_du_rrc_information_t cu2du = {0};
cu_to_du_rrc_information_t *cu2du_p = NULL;
if (UE->ue_cap_buffer.len > 0 && UE->ue_cap_buffer.buf != NULL) {
cu2du_p = &cu2du;
cu2du.uE_CapabilityRAT_ContainerList = UE->ue_cap_buffer.buf;
cu2du.uE_CapabilityRAT_ContainerList_length = UE->ue_cap_buffer.len;
}
f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
f1ap_ue_context_modif_req_t ue_context_modif_req = {
.gNB_CU_ue_id = UE->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.plmn.mcc = rrc->configuration.mcc[0],
.plmn.mnc = rrc->configuration.mnc[0],
.plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
.nr_cellid = rrc->nr_cellid,
.servCellId = 0, /* TODO: correct value? */
.srbs_to_be_setup_length = nb_srb,
.srbs_to_be_setup = srbs,
.drbs_to_be_setup_length = nb_drb,
.drbs_to_be_setup = drbs,
.cu_to_du_rrc_information = cu2du_p,
};
rrc->mac_rrc.ue_context_modification_request(&ue_context_modif_req);
}
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp)
{
gNB_RRC_INST *rrc = RC.nrrrc[0];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
if (ue_context_p == NULL) {
LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id);
return;
}
// there is not really anything to do here as of now
for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
}
}
void rrc_gNB_process_e1_bearer_context_release_cplt(const e1ap_bearer_release_cplt_t *cplt)
{
// there is not really anything to do here as of now
// note that we don't check for the UE: it does not exist anymore if the F1
// UE context release complete arrived from the DU first, after which we free
// the UE context
LOG_I(RRC, "UE %d: received bearer release complete\n", cplt->gNB_cu_cp_ue_id);
}
static void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id)
{
AssertFatal(rrc->du != NULL, "no DU connected, cannot received F1 lost connection\n");
AssertFatal(rrc->du->assoc_id == assoc_id,
"previously connected DU (%d) does not match DU for which connection has been lost (%d)\n",
rrc->du->assoc_id,
assoc_id);
(void) lc; // unused for the moment
ASN_STRUCT_FREE(asn_DEF_NR_MIB, rrc->du->mib);
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, rrc->du->sib1);
free(rrc->du);
rrc->du = NULL;
LOG_I(RRC, "dropping DU with assoc_id %d (UE connections remain, if any)\n", assoc_id);
}
static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
{
DevAssert(measresults->measResultServingMOList.list.count >= 1);
if (measresults->measResultServingMOList.list.count > 1)
LOG_W(RRC, "Received %d MeasResultServMO, but handling only 1!\n", measresults->measResultServingMOList.list.count);
NR_MeasResultServMO_t *measresultservmo = measresults->measResultServingMOList.list.array[0];
NR_MeasResultNR_t *measresultnr = &measresultservmo->measResultServingCell;
NR_MeasQuantityResults_t *mqr = measresultnr->measResult.cellResults.resultsSSB_Cell;
fprintf(f, " servingCellId %ld MeasResultNR for phyCellId %ld:\n resultSSB:", measresultservmo->servCellId, *measresultnr->physCellId);
if (mqr != NULL) {
const long rrsrp = *mqr->rsrp - 156;
const float rrsrq = (float) (*mqr->rsrq - 87) / 2.0f;
const float rsinr = (float) (*mqr->sinr - 46) / 2.0f;
fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr);
} else {
fprintf(f, "NOT PROVIDED\n");
}
}
static const char *get_rrc_connection_status_text(NR_UE_STATE_t state)
{
switch (state) {
case NR_RRC_INACTIVE: return "inactive";
case NR_RRC_IDLE: return "idle";
case NR_RRC_SI_RECEIVED: return "SI-received";
case NR_RRC_CONNECTED: return "connected";
case NR_RRC_RECONFIGURED: return "reconfigured";
case NR_RRC_HO_EXECUTION: return "HO-execution";
default: AssertFatal(false, "illegal RRC state %d\n", state); return "illegal";
}
return "illegal";
}
static const char *get_pdusession_status_text(pdu_session_status_t status)
{
switch (status) {
case PDU_SESSION_STATUS_NEW: return "new";
case PDU_SESSION_STATUS_DONE: return "done";
case PDU_SESSION_STATUS_ESTABLISHED: return "established";
case PDU_SESSION_STATUS_REESTABLISHED: return "reestablished";
case PDU_SESSION_STATUS_TOMODIFY: return "to-modify";
case PDU_SESSION_STATUS_FAILED: return "failed";
case PDU_SESSION_STATUS_TORELEASE: return "to-release";
case PDU_SESSION_STATUS_RELEASED: return "released";
default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal";
}
return "illegal";
}
static void write_rrc_stats(const gNB_RRC_INST *rrc)
{
const char *filename = "nrRRC_stats.log";
FILE *f = fopen(filename, "w");
if (f == NULL) {
LOG_E(NR_RRC, "cannot open %s for writing\n", filename);
return;
}
int i = 0;
rrc_gNB_ue_context_t *ue_context_p = NULL;
/* cast is necessary to eliminate warning "discards ‘const’ qualifier" */
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &((gNB_RRC_INST *)rrc)->rrc_ue_head)
{
const gNB_RRC_UE_t *ue_ctxt = &ue_context_p->ue_context;
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_ctxt->rrc_ue_id);
/* currently, we support only one DU. If we support multiple, need to
* search for the DU corresponding to this UE here */
const nr_rrc_du_container_t *du = rrc->du;
DevAssert(du != NULL);
fprintf(f,
"UE %d CU UE ID %d DU UE ID %d RNTI %04x random identity %016lx",
i,
ue_ctxt->rrc_ue_id,
ue_data.secondary_ue,
ue_ctxt->rnti,
ue_ctxt->random_ue_identity);
if (ue_ctxt->Initialue_identity_5g_s_TMSI.presence)
fprintf(f, " S-TMSI %x", ue_ctxt->Initialue_identity_5g_s_TMSI.fiveg_tmsi);
fprintf(f, ":\n");
fprintf(f, " RRC status %s\n", get_rrc_connection_status_text(ue_ctxt->StatusRrc));
if (ue_ctxt->nb_of_pdusessions == 0)
fprintf(f, " (no PDU sessions)\n");
for (int nb_pdu = 0; nb_pdu < ue_ctxt->nb_of_pdusessions; ++nb_pdu) {
const rrc_pdu_session_param_t *pdu = &ue_ctxt->pduSession[nb_pdu];
fprintf(f, " PDU session %d ID %d status %s\n", nb_pdu, pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
}
if (ue_ctxt->UE_Capability_nr) {
AssertFatal(du->setup_req->num_cells_available == 1, "only one cell supported at the moment\n");
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
fprintf(f,
" UE cap: BW DL %x. BW UL %x, DL MIMO Layers %d UL MIMO Layers (CB) %d UL MIMO Layers (nonCB) %d\n",
get_dl_bw_mask(cell_info, ue_ctxt->UE_Capability_nr),
get_ul_bw_mask(cell_info, ue_ctxt->UE_Capability_nr),
get_dl_mimo_layers(cell_info, ue_ctxt->UE_Capability_nr),
get_ul_mimo_layersCB(cell_info, ue_ctxt->UE_Capability_nr),
get_ul_mimo_layers(cell_info, ue_ctxt->UE_Capability_nr));
}
if (ue_ctxt->measResults)
print_rrc_meas(f, ue_ctxt->measResults);
++i;
}
fclose(f);
}
void *rrc_gnb_task(void *args_p) {
MessageDef *msg_p;
instance_t instance;
int result;
protocol_ctxt_t ctxt = {.module_id = 0, .enb_flag = 1, .instance = 0, .rntiMaybeUEid = 0, .frame = -1, .subframe = -1, .eNB_index = 0, .brOption = false};
long stats_timer_id = 1;
if (!IS_SOFTMODEM_NOSTATS_BIT) {
/* timer to write stats to file */
timer_setup(1, 0, TASK_RRC_GNB, 0, TIMER_PERIODIC, NULL, &stats_timer_id);
}
itti_mark_task_ready(TASK_RRC_GNB);
LOG_I(NR_RRC,"Entering main loop of NR_RRC message task\n");
while (1) {
// Wait for a message
itti_receive_msg(TASK_RRC_GNB, &msg_p);
const char *msg_name_p = ITTI_MSG_NAME(msg_p);
instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
LOG_D(NR_RRC, "Received Msg %s\n", msg_name_p);
switch (ITTI_MSG_ID(msg_p)) {
case TERMINATE_MESSAGE:
LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n");
itti_exit_task();
break;
case MESSAGE_TEST:
LOG_I(NR_RRC, "[gNB %ld] Received %s\n", instance, msg_name_p);
break;
case TIMER_HAS_EXPIRED:
/* only this one handled for now */
DevAssert(TIMER_HAS_EXPIRED(msg_p).timer_id == stats_timer_id);
write_rrc_stats(RC.nrrrc[0]);
break;
case F1AP_INITIAL_UL_RRC_MESSAGE:
AssertFatal(NODE_IS_CU(RC.nrrrc[instance]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[instance]->node_type),
"should not receive F1AP_INITIAL_UL_RRC_MESSAGE, need call by CU!\n");
rrc_gNB_process_initial_ul_rrc_message(&F1AP_INITIAL_UL_RRC_MESSAGE(msg_p));
break;
/* Messages from PDCP */
case F1AP_UL_RRC_MESSAGE:
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
instance,
GNB_FLAG_YES,
F1AP_UL_RRC_MESSAGE(msg_p).gNB_CU_ue_id,
0,
0);
LOG_D(NR_RRC,
"Decoding DCCH %d: ue %04lx, inst %ld, ctxt %p, size %d\n",
F1AP_UL_RRC_MESSAGE(msg_p).srb_id,
ctxt.rntiMaybeUEid,
instance,
&ctxt,
F1AP_UL_RRC_MESSAGE(msg_p).rrc_container_length);
rrc_gNB_decode_dcch(&ctxt,
F1AP_UL_RRC_MESSAGE(msg_p).srb_id,
F1AP_UL_RRC_MESSAGE(msg_p).rrc_container,
F1AP_UL_RRC_MESSAGE(msg_p).rrc_container_length);
free(F1AP_UL_RRC_MESSAGE(msg_p).rrc_container);
break;
case NGAP_DOWNLINK_NAS:
rrc_gNB_process_NGAP_DOWNLINK_NAS(msg_p, instance, &rrc_gNB_mui);
break;
case NGAP_PDUSESSION_SETUP_REQ:
rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance);
break;
case NGAP_PDUSESSION_MODIFY_REQ:
rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(msg_p, instance);
break;
case NGAP_PDUSESSION_RELEASE_COMMAND:
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
break;
/* Messages from F1AP task */
case F1AP_SETUP_REQ:
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p), msg_p->ittiMsgHeader.originInstance);
break;
case F1AP_UE_CONTEXT_SETUP_RESP:
rrc_CU_process_ue_context_setup_response(msg_p, instance);
break;
case F1AP_UE_CONTEXT_MODIFICATION_RESP:
rrc_CU_process_ue_context_modification_response(msg_p, instance);
break;
case F1AP_UE_CONTEXT_MODIFICATION_REQUIRED:
rrc_CU_process_ue_modification_required(msg_p);
break;
case F1AP_UE_CONTEXT_RELEASE_REQ:
rrc_CU_process_ue_context_release_request(msg_p);
break;
case F1AP_UE_CONTEXT_RELEASE_COMPLETE:
rrc_CU_process_ue_context_release_complete(msg_p);
break;
case F1AP_LOST_CONNECTION:
rrc_CU_process_f1_lost_connection(RC.nrrrc[0], &F1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
break;
/* Messages from X2AP */
case X2AP_ENDC_SGNB_ADDITION_REQ:
LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n");
rrc_gNB_process_AdditionRequestInformation(instance, &X2AP_ENDC_SGNB_ADDITION_REQ(msg_p));
break;
case X2AP_ENDC_SGNB_RECONF_COMPLETE:
LOG_A(NR_RRC, "Handling of reconfiguration complete message at RRC gNB is pending \n");
break;
case NGAP_INITIAL_CONTEXT_SETUP_REQ:
rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p, instance);
break;
case X2AP_ENDC_SGNB_RELEASE_REQUEST:
LOG_I(NR_RRC, "Received ENDC sgNB release request from X2AP \n");
rrc_gNB_process_release_request(instance, &X2AP_ENDC_SGNB_RELEASE_REQUEST(msg_p));
break;
case X2AP_ENDC_DC_OVERALL_TIMEOUT:
rrc_gNB_process_dc_overall_timeout(instance, &X2AP_ENDC_DC_OVERALL_TIMEOUT(msg_p));
break;
case NGAP_UE_CONTEXT_RELEASE_REQ:
rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(msg_p, instance);
break;
case NGAP_UE_CONTEXT_RELEASE_COMMAND:
rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p, instance);
break;
case E1AP_SETUP_REQ:
rrc_gNB_process_e1_setup_req(msg_p->ittiMsgHeader.originInstance, &E1AP_SETUP_REQ(msg_p));
break;
case E1AP_BEARER_CONTEXT_SETUP_RESP:
rrc_gNB_process_e1_bearer_context_setup_resp(&E1AP_BEARER_CONTEXT_SETUP_RESP(msg_p), instance);
break;
case E1AP_BEARER_CONTEXT_MODIFICATION_RESP:
rrc_gNB_process_e1_bearer_context_modif_resp(&E1AP_BEARER_CONTEXT_MODIFICATION_RESP(msg_p));
break;
case E1AP_BEARER_CONTEXT_RELEASE_CPLT:
rrc_gNB_process_e1_bearer_context_release_cplt(&E1AP_BEARER_CONTEXT_RELEASE_CPLT(msg_p));
break;
case NGAP_PAGING_IND:
rrc_gNB_process_PAGING_IND(msg_p, instance);
break;
default:
LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
break;
}
result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
msg_p = NULL;
}
}
typedef struct deliver_ue_ctxt_setup_data_t {
gNB_RRC_INST *rrc;
f1ap_ue_context_setup_t *setup_req;
} deliver_ue_ctxt_setup_data_t;
static void rrc_deliver_ue_ctxt_setup_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
deliver_ue_ctxt_setup_data_t *data = deliver_pdu_data;
data->setup_req->rrc_container = (uint8_t*)buf;
data->setup_req->rrc_container_length = size;
data->rrc->mac_rrc.ue_context_setup_request(data->setup_req);
}
//-----------------------------------------------------------------------------
void
rrc_gNB_generate_SecurityModeCommand(
const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *const ue_context_pP
)
//-----------------------------------------------------------------------------
{
uint8_t buffer[100];
uint8_t size;
gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
T(T_ENB_RRC_SECURITY_MODE_COMMAND, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rntiMaybeUEid));
NR_IntegrityProtAlgorithm_t integrity_algorithm = (NR_IntegrityProtAlgorithm_t)ue_p->integrity_algorithm;
size = do_NR_SecurityModeCommand(ctxt_pP, buffer, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), ue_p->ciphering_algorithm, integrity_algorithm);
LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Security Mode Command\n");
LOG_I(NR_RRC, "UE %04x Logical Channel DL-DCCH, Generate SecurityModeCommand (bytes %d)\n", ue_p->rnti, size);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
cu_to_du_rrc_information_t cu2du = {0};
cu_to_du_rrc_information_t *cu2du_p = NULL;
if (ue_p->ue_cap_buffer.len > 0 && ue_p->ue_cap_buffer.buf != NULL) {
cu2du_p = &cu2du;
cu2du.uE_CapabilityRAT_ContainerList = ue_p->ue_cap_buffer.buf;
cu2du.uE_CapabilityRAT_ContainerList_length = ue_p->ue_cap_buffer.len;
}
const nr_rrc_du_container_t *du = rrc->du;
DevAssert(du != NULL);
/* the callback will fill the UE context setup request and forward it */
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
f1ap_ue_context_setup_t ue_context_setup_req = {
.gNB_CU_ue_id = ue_p->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.plmn.mcc = rrc->configuration.mcc[0],
.plmn.mnc = rrc->configuration.mnc[0],
.plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
.nr_cellid = rrc->nr_cellid,
.servCellId = 0, /* TODO: correct value? */
.srbs_to_be_setup = 0, /* no new SRBs */
.drbs_to_be_setup = 0, /* no new DRBs */
.cu_to_du_rrc_information = cu2du_p,
};
deliver_ue_ctxt_setup_data_t data = {.rrc = rrc, .setup_req = &ue_context_setup_req};
nr_pdcp_data_req_srb(ctxt_pP->rntiMaybeUEid, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_setup_req, &data);
}
void
rrc_gNB_generate_UECapabilityEnquiry(
const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *const ue_context_pP
)
//-----------------------------------------------------------------------------
{
uint8_t buffer[100];
uint8_t size;
T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rntiMaybeUEid));
size = do_NR_SA_UECapabilityEnquiry(
ctxt_pP,
buffer,
rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id));
LOG_I(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d)\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
size);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
const gNB_RRC_UE_t *ue = &ue_context_pP->ue_context;
nr_rrc_transfer_protected_rrc_message(rrc, ue, DCCH, buffer, size);
}
typedef struct deliver_ue_ctxt_release_data_t {
gNB_RRC_INST *rrc;
f1ap_ue_context_release_cmd_t *release_cmd;
} deliver_ue_ctxt_release_data_t;
static void rrc_deliver_ue_ctxt_release_cmd(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
deliver_ue_ctxt_release_data_t *data = deliver_pdu_data;
data->release_cmd->rrc_container = (uint8_t*) buf;
data->release_cmd->rrc_container_length = size;
data->rrc->mac_rrc.ue_context_release_command(data->release_cmd);
}
//-----------------------------------------------------------------------------
/*
* Generate the RRC Connection Release to UE.
* If received, UE should switch to RRC_IDLE mode.
*/
void
rrc_gNB_generate_RRCRelease(
const protocol_ctxt_t *const ctxt_pP,
rrc_gNB_ue_context_t *const ue_context_pP
)
//-----------------------------------------------------------------------------
{
uint8_t buffer[RRC_BUF_SIZE] = {0};
int size = do_NR_RRCRelease(buffer, RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id));
LOG_I(NR_RRC,
PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate RRCRelease (bytes %d)\n",
PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
size);
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
const gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
f1ap_ue_context_release_cmd_t ue_context_release_cmd = {
.gNB_CU_ue_id = UE->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.cause = F1AP_CAUSE_RADIO_NETWORK,
.cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release
.srb_id = DCCH,
};
deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd};
nr_pdcp_data_req_srb(ctxt_pP->rntiMaybeUEid, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data);
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
e1ap_bearer_release_cmd_t cmd = {
.gNB_cu_cp_ue_id = UE->rrc_ue_id,
.gNB_cu_up_ue_id = UE->rrc_ue_id,
};
rrc->cucp_cuup.bearer_context_release(assoc_id, &cmd);
/* UE will be freed after UE context release complete */
}
void rrc_gNB_trigger_new_bearer(int rnti)
{
/* get RRC and UE */
gNB_RRC_INST *rrc = RC.nrrrc[0];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, rnti);
if (ue_context_p == NULL) {
LOG_E(RRC, "unknown UE RNTI %04x\n", rnti);
return;
}
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
/* get the existing PDU sessoin */
if (ue->nb_of_pdusessions < 1) {
LOG_E(RRC, "no PDU session set up yet, cannot create additional bearer\n");
return;
}
if (ue->established_drbs[0].status != DRB_INACTIVE
&& ue->established_drbs[1].status != DRB_INACTIVE) {
LOG_E(RRC, "already have two established bearers, aborting\n");
return;
}
e1ap_bearer_setup_req_t bearer_req = {0};
bearer_req.gNB_cu_cp_ue_id = ue->rrc_ue_id;
bearer_req.cipheringAlgorithm = ue->ciphering_algorithm;
memcpy(bearer_req.encryptionKey, ue->kgnb, sizeof(ue->kgnb));
bearer_req.integrityProtectionAlgorithm = ue->integrity_algorithm;
memcpy(bearer_req.integrityProtectionKey, ue->kgnb, sizeof(ue->kgnb));
bearer_req.ueDlAggMaxBitRate = 10000; /* probably does not matter */
pdu_session_to_setup_t *pdu = &bearer_req.pduSession[0];
//bearer_req.numPDUSessions++;
bearer_req.numPDUSessions = 1;
//pdu->sessionId = session->pdusession_id;
//pdu->sst = msg->allowed_nssai[i].sST;
//pdu->integrityProtectionIndication = rrc->security.do_drb_integrity ? E1AP_IntegrityProtectionIndication_required : E1AP_IntegrityProtectionIndication_not_needed;
//pdu->confidentialityProtectionIndication = rrc->security.do_drb_ciphering ? E1AP_ConfidentialityProtectionIndication_required : E1AP_ConfidentialityProtectionIndication_not_needed;
//pdu->teId = session->gtp_teid;
pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding
DRB_nGRAN_to_setup_t *drb = &pdu->DRBnGRanList[0];
int drb_id = 2;
drb->id = drb_id;
drb->defaultDRB = E1AP_DefaultDRB_false;
drb->sDAP_Header_UL = !(rrc->configuration.enable_sdap);
drb->sDAP_Header_DL = !(rrc->configuration.enable_sdap);
drb->pDCP_SN_Size_UL = E1AP_PDCP_SN_Size_s_18;
drb->pDCP_SN_Size_DL = E1AP_PDCP_SN_Size_s_18;
drb->discardTimer = E1AP_DiscardTimer_infinity;
drb->reorderingTimer = E1AP_T_Reordering_ms0;
drb->rLC_Mode = E1AP_RLC_Mode_rlc_am;
drb->numCellGroups = 1; // assume one cell group associated with a DRB
for (int k=0; k < drb->numCellGroups; k++) {
cell_group_t *cellGroup = drb->cellGroupList + k;
cellGroup->id = 0; // MCG
}
int xid = rrc_gNB_get_next_transaction_identifier(0);
/* generate a new bearer, it will be put into internal RRC state and picked
* up later */
generateDRB(ue,
drb_id,
&ue->pduSession[0],
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering);
/* associate the new bearer to it */
ue->xids[xid] = RRC_PDUSESSION_MODIFY;
ue->pduSession[0].xid = xid; // hack: fake xid for ongoing PDU session
LOG_W(RRC, "trigger new bearer %ld for UE %04x xid %d\n", drb->id, ue->rnti, xid);
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue);
rrc->cucp_cuup.bearer_context_setup(assoc_id, &bearer_req);
}
void rrc_gNB_trigger_release_bearer(int rnti)
{
/* get RRC and UE */
gNB_RRC_INST *rrc = RC.nrrrc[0];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, rnti);
if (ue_context_p == NULL) {
LOG_E(RRC, "unknown UE RNTI %04x\n", rnti);
return;
}
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
if (ue->established_drbs[1].status == DRB_INACTIVE) {
LOG_E(RRC, "no second bearer, aborting\n");
return;
}
// don't use E1: bearer release is not implemented, call directly
// into PDCP/SDAP and then send corresponding message via F1
int drb_id = 2;
ue->established_drbs[1].status = DRB_INACTIVE;
ue->DRB_ReleaseList = calloc(1, sizeof(*ue->DRB_ReleaseList));
AssertFatal(ue->DRB_ReleaseList != NULL, "out of memory\n");
NR_DRB_Identity_t *asn1_drb = malloc(sizeof(*asn1_drb));
AssertFatal(asn1_drb != NULL, "out of memory\n");
int idx = 0;
NR_DRB_ToAddModList_t *drb_list = createDRBlist(ue, false);
while (idx < drb_list->list.count) {
const NR_DRB_ToAddMod_t *drbc = drb_list->list.array[idx];
if (drbc->drb_Identity == drb_id)
break;
++idx;
}
if (idx < drb_list->list.count) {
nr_pdcp_release_drb(rnti, drb_id);
asn_sequence_del(&drb_list->list, idx, 1);
}
*asn1_drb = drb_id;
asn1cSeqAdd(&ue->DRB_ReleaseList->list, asn1_drb);
f1ap_drb_to_be_released_t drbs_to_be_released[1] = {{.rb_id = drb_id}};
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
f1ap_ue_context_modif_req_t ue_context_modif_req = {
.gNB_CU_ue_id = ue->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.plmn.mcc = rrc->configuration.mcc[0],
.plmn.mnc = rrc->configuration.mnc[0],
.plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
.nr_cellid = rrc->nr_cellid,
.servCellId = 0, /* TODO: correct value? */
.srbs_to_be_setup_length = 0,
.srbs_to_be_setup = NULL,
.drbs_to_be_setup_length = 0,
.drbs_to_be_setup = NULL,
.drbs_to_be_released_length = 1,
.drbs_to_be_released = drbs_to_be_released,
};
rrc->mac_rrc.ue_context_modification_request(&ue_context_modif_req);
}
int rrc_gNB_generate_pcch_msg(uint32_t tmsi, uint8_t paging_drx, instance_t instance, uint8_t CC_id){
const unsigned int Ttab[4] = {32,64,128,256};
uint8_t Tc;
uint8_t Tue;
uint32_t pfoffset;
uint32_t N; /* N: min(T,nB). total count of PF in one DRX cycle */
uint32_t Ns = 0; /* Ns: max(1,nB/T) */
uint8_t i_s; /* i_s = floor(UE_ID/N) mod Ns */
uint32_t T; /* DRX cycle */
uint32_t length;
uint8_t buffer[RRC_BUF_SIZE];
const nr_rrc_du_container_t *du = RC.nrrrc[0]->du;
DevAssert(du != NULL);
struct NR_SIB1 *sib1 = du->sib1;
/* get default DRX cycle from configuration */
Tc = sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.defaultPagingCycle;
Tue = paging_drx;
/* set T = min(Tc,Tue) */
T = Tc < Tue ? Ttab[Tc] : Ttab[Tue];
/* set N = PCCH-Config->nAndPagingFrameOffset */
switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present) {
case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneT:
N = T;
pfoffset = 0;
break;
case NR_PCCH_Config__nAndPagingFrameOffset_PR_halfT:
N = T/2;
pfoffset = 1;
break;
case NR_PCCH_Config__nAndPagingFrameOffset_PR_quarterT:
N = T/4;
pfoffset = 3;
break;
case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneEighthT:
N = T/8;
pfoffset = 7;
break;
case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneSixteenthT:
N = T/16;
pfoffset = 15;
break;
default:
LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: pfoffset error (pfoffset %d)\n",
instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present);
return (-1);
}
switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns) {
case NR_PCCH_Config__ns_four:
if(*sib1->servingCellConfigCommon->downlinkConfigCommon.initialDownlinkBWP.pdcch_ConfigCommon->choice.setup->pagingSearchSpace == 0){
LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: ns error only 1 or 2 is allowed when pagingSearchSpace is 0\n",
instance);
return (-1);
} else {
Ns = 4;
}
break;
case NR_PCCH_Config__ns_two:
Ns = 2;
break;
case NR_PCCH_Config__ns_one:
Ns = 1;
break;
default:
LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: ns error (ns %ld)\n",
instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns);
return (-1);
}
/* insert data to UE_PF_PO or update data in UE_PF_PO */
pthread_mutex_lock(&ue_pf_po_mutex);
uint8_t i = 0;
for (i = 0; i < MAX_MOBILES_PER_ENB; i++) {
if ((UE_PF_PO[CC_id][i].enable_flag == true && UE_PF_PO[CC_id][i].ue_index_value == (uint16_t)(tmsi%1024))
|| (UE_PF_PO[CC_id][i].enable_flag != true)) {
/* set T = min(Tc,Tue) */
UE_PF_PO[CC_id][i].T = T;
/* set UE_ID */
UE_PF_PO[CC_id][i].ue_index_value = (uint16_t)(tmsi%1024);
/* calculate PF and PO */
/* set PF_min and PF_offset: (SFN + PF_offset) mod T = (T div N)*(UE_ID mod N) */
UE_PF_PO[CC_id][i].PF_min = (T / N) * (UE_PF_PO[CC_id][i].ue_index_value % N);
UE_PF_PO[CC_id][i].PF_offset = pfoffset;
/* set i_s */
/* i_s = floor(UE_ID/N) mod Ns */
i_s = (uint8_t)((UE_PF_PO[CC_id][i].ue_index_value / N) % Ns);
UE_PF_PO[CC_id][i].i_s = i_s;
// TODO,set PO
if (UE_PF_PO[CC_id][i].enable_flag == true) {
//paging exist UE log
LOG_D(NR_RRC,"[gNB %ld] CC_id %d In rrc_gNB_generate_pcch_msg: Update exist UE %d, T %d, N %d, PF %d, i_s %d, PF_offset %d\n", instance, CC_id, UE_PF_PO[CC_id][i].ue_index_value,
T, N, UE_PF_PO[CC_id][i].PF_min, UE_PF_PO[CC_id][i].i_s, UE_PF_PO[CC_id][i].PF_offset);
} else {
/* set enable_flag */
UE_PF_PO[CC_id][i].enable_flag = true;
//paging new UE log
LOG_D(NR_RRC,"[gNB %ld] CC_id %d In rrc_gNB_generate_pcch_msg: Insert a new UE %d, T %d, N %d, PF %d, i_s %d, PF_offset %d\n", instance, CC_id, UE_PF_PO[CC_id][i].ue_index_value,
T, N, UE_PF_PO[CC_id][i].PF_min, UE_PF_PO[CC_id][i].i_s, UE_PF_PO[CC_id][i].PF_offset);
}
break;
}
}
pthread_mutex_unlock(&ue_pf_po_mutex);
/* Create message for PDCP (DLInformationTransfer_t) */
length = do_NR_Paging (instance,
buffer,
tmsi);
if (length == -1) {
LOG_I(NR_RRC, "do_Paging error\n");
return -1;
}
// TODO, send message to pdcp
return 0;
}