Files
openairinterface5g/openair2/RRC/NR_UE/rrc_UE.c
Guido Casati e179a6df8f fix (UE RRC): queue MAC reset before RLC teardown on RRCSetup fallback
When the gNB answers RRCReestablishmentRequest with RRCSetup (TS 38.331
clause 5.3.3.4 fallback), the UE must release all bearers except SRB0 before
applying the new masterCellGroup. CONFIG_RESET was queued after RLC release,
so the MAC slot thread kept polling LCID 1 from a stale lc_ordered_list while
the RLC entity was already NULL.

Queue NR_MAC_RRC_CONFIG_RESET (RRC_SETUP_REESTAB_RESUME) first, then release
PDCP, RLC, and SDAP. On the MAC thread, clear lc_ordered_list except SRB0
before release_mac_configuration() so the UL scheduler stops looping released
logical channels until CONFIG_CG re-adds SRB1.

Changes:
- rrc_UE.c: move CONFIG_RESET to the start of nr_rrc_rrcsetup_fallback()
- config_ue.c: drop non-SRB0 lc_ordered_list entries on RRC_SETUP_REESTAB_RESUME

Closes: #128

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00

3739 lines
160 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.
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*
* \brief RRC procedures
*/
#define RRC_UE
#define RRC_UE_C
#include "LTE_MeasObjectToAddMod.h"
#include "NR_DL-DCCH-Message.h" //asn_DEF_NR_DL_DCCH_Message
#include "NR_DL-CCCH-Message.h" //asn_DEF_NR_DL_CCCH_Message
#include "NR_BCCH-BCH-Message.h" //asn_DEF_NR_BCCH_BCH_Message
#include "NR_BCCH-DL-SCH-Message.h" //asn_DEF_NR_BCCH_DL_SCH_Message
#include "NR_PCCH-Message.h" //asn_DEF_NR_PCCH_Message
#include "NR_CellGroupConfig.h" //asn_DEF_NR_CellGroupConfig
#include "NR_BWP-Downlink.h" //asn_DEF_NR_BWP_Downlink
#include "NR_RRCReconfiguration.h"
#include "NR_MeasConfig.h"
#include "NR_UL-DCCH-Message.h"
#include "uper_encoder.h"
#include "uper_decoder.h"
#include "rrc_defs.h"
#include "rrc_proto.h"
#include "verify_RRC.h"
#include "L2_interface_ue.h"
#include "LAYER2/NR_MAC_UE/mac_proto.h"
#include "intertask_interface.h"
#include "LAYER2/nr_rlc/nr_rlc_oai_api.h"
#include "nr-uesoftmodem.h"
#include "plmn_data.h"
#include "nr_pdcp/nr_pdcp_oai_api.h"
#include "openair3/SECU/secu_defs.h"
#include "openair3/SECU/key_nas_deriver.h"
#include "common/utils/LOG/log.h"
#include "conversions.h"
#include "common/utils/ds/byte_array.h"
#ifndef CELLULAR
#include "RRC/NR/MESSAGES/asn1_msg.h"
#endif
#include "SIMULATION/TOOLS/sim.h" // for taus
#include "nr_nas_msg.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
static NR_UE_RRC_INST_t *NR_UE_rrc_inst[MAX_NUM_NR_UE_INST] = {0};
/* NAS Attach request with IMSI */
static const char nr_nas_attach_req_imsi_dummy_NSA_case[] = {
0x07,
0x41,
/* EPS Mobile identity = IMSI */
0x71,
0x08,
0x29,
0x80,
0x43,
0x21,
0x43,
0x65,
0x87,
0xF9,
/* End of EPS Mobile Identity */
0x02,
0xE0,
0xE0,
0x00,
0x20,
0x02,
0x03,
0xD0,
0x11,
0x27,
0x1A,
0x80,
0x80,
0x21,
0x10,
0x01,
0x00,
0x00,
0x10,
0x81,
0x06,
0x00,
0x00,
0x00,
0x00,
0x83,
0x06,
0x00,
0x00,
0x00,
0x00,
0x00,
0x0D,
0x00,
0x00,
0x0A,
0x00,
0x52,
0x12,
0xF2,
0x01,
0x27,
0x11,
};
/**
* @brief Sends an RRC message to the connected UE MAC instance.
*
* @param rrc UE RRC instance structure
* @param msg RRC message to be sent to MAC
*/
static void nr_rrc_send_msg_to_mac(NR_UE_RRC_INST_t *rrc, nr_mac_rrc_message_t *msg)
{
AssertFatal(rrc->mac_input_nf != NULL, "MAC input NF is NULL for UE %ld\n", rrc->ue_id);
notifiedFIFO_elt_t *nf_msg = newNotifiedFIFO_elt(sizeof(nr_mac_rrc_message_t), 0, NULL, NULL);
nr_mac_rrc_message_t *rrc_msg = NotifiedFifoData(nf_msg);
memcpy(rrc_msg, msg, sizeof(nr_mac_rrc_message_t));
pushNotifiedFIFO(rrc->mac_input_nf, nf_msg);
}
/** @brief Ask MAC to start or restart random access
* @param rrc UE RRC instance
* @param cause Why RA is started (setup, T300, post-SIB, re-establishment) */
static void nr_rrc_trigger_mac_ra(NR_UE_RRC_INST_t *rrc, nr_mac_ra_start_cause_t cause)
{
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_START_RA;
rrc_msg.payload.start_ra.cause = cause;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
NR_UE_RRC_INST_t *get_NR_UE_rrc_inst(int instance)
{
AssertFatal(instance >= 0 && instance < MAX_NUM_NR_UE_INST, "RRC instance %d out of bounds\n", instance);
NR_UE_RRC_INST_t *rrc = NR_UE_rrc_inst[instance];
if (rrc == NULL)
return NULL;
AssertFatal(rrc->ue_id == instance, "RRC ID %d doesn't match with input %d\n", (int)rrc->ue_id, instance);
return rrc;
}
static NR_RB_status_t get_DRB_status(const NR_UE_RRC_INST_t *rrc, NR_DRB_Identity_t drb_id)
{
AssertFatal(drb_id > 0 && drb_id < 33, "Invalid DRB ID %ld\n", drb_id);
return rrc->status_DRBs[drb_id - 1];
}
static void set_DRB_status(NR_UE_RRC_INST_t *rrc, NR_DRB_Identity_t drb_id, NR_RB_status_t status)
{
AssertFatal(drb_id > 0 && drb_id < 33, "Invalid DRB ID %ld\n", drb_id);
rrc->status_DRBs[drb_id - 1] = status;
}
static int get_ulsyncvalidityduration_timer_value(NR_NTN_Config_r17_t *ntncfg)
{
int retval = 0;
AssertFatal(ntncfg, "NTN-Config IE not present\n");
if (ntncfg->ntn_UlSyncValidityDuration_r17) {
const int values[] = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 120, 180, 240, 900};
retval = values[*ntncfg->ntn_UlSyncValidityDuration_r17];
}
return retval;
}
static void get_epochtime_from_sib19scheduling(NR_UE_RRC_SI_INFO *SI_info, int *frame, int *subframe)
{
// TS 38.331 section 5.2.2.3.2
// SI-window starts at the slot #a, where a = x mod N
// x = (si-WindowPosition -1) × w, where w is the si-WindowLength;
// N is the number of slots in a radio frame
int wl_slots = 5 << SI_info->si_windowlength;
int x = (SI_info->sib19_windowposition - 1) * wl_slots;
int slots_per_subframe = 1 << SI_info->scs;
int N = slots_per_subframe * 10;
int slot_end_sib19_window = (x % N) + wl_slots;
*frame += (slot_end_sib19_window / N);
int slot = slot_end_sib19_window % N;
*subframe = ceil(slot / slots_per_subframe);
LOG_I(NR_RRC,
"Get EPOCHTIME: x:%d, N:%d, slot_endw:%d, frame:%d, subframe:%d , slot:%d\n",
x,
N,
slot_end_sib19_window,
*frame,
*subframe,
slot);
}
static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell)
{
int epoch_frame = 0, epoch_subframe = 0;
int diff_frames = 0;
AssertFatal(frame >= 0, "Frame Incorrect, results in incorrect EPOCH time evaluation\n");
if (ntncfg->epochTime_r17) {
epoch_frame = ntncfg->epochTime_r17->sfn_r17;
epoch_subframe = ntncfg->epochTime_r17->subFrameNR_r17;
} else {
// EPOCH time is optional in case of SIB19. This case happens only in case of SIB19 NTN config
// If no EPOCH time is sent, epochtime points to SIB19 window end slot in the current scheduling window
epoch_frame = frame;
epoch_subframe = 0;
get_epochtime_from_sib19scheduling(SI_info, &epoch_frame, &epoch_subframe);
// Adding epochTime IE in SIB19, later MAC will use it.
ntncfg->epochTime_r17 = CALLOC(1, sizeof(NR_EpochTime_r17_t));
ntncfg->epochTime_r17->sfn_r17 = epoch_frame;
ntncfg->epochTime_r17->subFrameNR_r17 = epoch_subframe;
}
if (is_targetcell) {
// For Target cell,the SFN nearest to the frame needs to be considered
// i.e. Epochframe can be in the past or future
int diff1 = (frame - epoch_frame + 1024) % 1024;
int diff2 = (epoch_frame - frame + 1024) % 1024;
diff_frames = (diff1 < diff2) ? -diff1 : diff2;
} else {
// For serving cell, the field sfn indicates the current SFN or the next upcoming SFN
// after the frame where the message indicating the epochTime is received
// i.e. Epochframe can be present or future SFN
diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this
}
LOG_I(NR_RRC, "Epoch frame %d ahead by %d frames\n", epoch_frame, diff_frames);
return diff_frames;
}
static int get_ntn_timervalues(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int diff_frames, int *val430_ms)
{
int val430 = get_ulsyncvalidityduration_timer_value(ntncfg);
int sib19_periodicity_ms = SI_info->sib19_periodicity * 10;
*val430_ms = val430 * 1000 + diff_frames * 10; // in ms
if (*val430_ms <= sib19_periodicity_ms)
LOG_E(NR_RRC, "Too small T430 value. Might result in frequent ULSYNC failure\n");
// by default SIB19 reception is started from the middle of the ulsyncvalidity duration.(i.e val430 in ms / 2)
int sib19_timer_ms = val430 * 500 + diff_frames * 10;
// if this is less than the SIB19 periodicity, use that instead.
// set the timer to expire 1 frame (10 ms) before periodicity, to not miss the SIB19
if (sib19_timer_ms <= sib19_periodicity_ms)
sib19_timer_ms = sib19_periodicity_ms - 10;
LOG_I(NR_RRC, "val430:%d s, T430:%d ms, sib19_timer:%d ms\n", val430, *val430_ms, sib19_timer_ms);
return sib19_timer_ms;
}
static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell)
{
SI_info->SInfo_r17.sib19_validity = true;
// Check if Epochtime is sent or not
int diff_frames = eval_epoch_time(SI_info, ntncfg, frame, is_targetcell);
if (ntncfg->ntn_UlSyncValidityDuration_r17) { // ulsyncvalidity duration configured
int val430_ms = 0, sib19_timer_ms = 0;
sib19_timer_ms = get_ntn_timervalues(SI_info, ntncfg, diff_frames, &val430_ms);
// T430 should be started only in connected mode.
// Inorder to avoid starting T430 when entering connected mode, T430 is started as soon as
// SIB19 is received, and if UE enters connected mode T430 will be in running.
// T430 expiry in RRC idle or inactive states does nothing.
nr_timer_setup(&rrc->timers_and_constants.T430, val430_ms, 10);
nr_timer_start(&rrc->timers_and_constants.T430);
// SIB19 should be received before T430 expires
// SIB19 validity timer should expire before T430 expiry such that new SIB19 is read
if (sib19_timer_ms > 0) {
nr_timer_setup(&SI_info->SInfo_r17.sib19_timer, sib19_timer_ms, 10);
nr_timer_start(&SI_info->SInfo_r17.sib19_timer);
} else
// This makes sure that SIB19 is read again in the next window
SI_info->SInfo_r17.sib19_validity = false;
} else
nr_timer_start(&SI_info->SInfo_r17.sib19_timer);
}
static void nr_decode_SI(NR_UE_RRC_SI_INFO *SI_info, NR_SystemInformation_t *si, NR_UE_RRC_INST_t *rrc, int hfn, int frame)
{
// Dump contents
if (si->criticalExtensions.present == NR_SystemInformation__criticalExtensions_PR_systemInformation
|| si->criticalExtensions.present == NR_SystemInformation__criticalExtensions_PR_criticalExtensionsFuture_r16) {
LOG_D(NR_RRC,
"[UE] si->criticalExtensions.choice.NR_SystemInformation_t->sib_TypeAndInfo.list.count %d\n",
si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.count);
} else {
LOG_D(NR_RRC, "[UE] Unknown criticalExtension version (not Rel16)\n");
return;
}
NR_SIB19_r17_t *sib19 = NULL;
for (int i = 0; i < si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.count; i++) {
SystemInformation_IEs__sib_TypeAndInfo__Member *typeandinfo;
typeandinfo = si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.array[i];
LOG_A(NR_RRC, "Found SIB%d\n", typeandinfo->present + 1);
switch(typeandinfo->present) {
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2:
SI_info->sib2_validity = true;
nr_timer_start(&SI_info->sib2_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3:
SI_info->sib3_validity = true;
nr_timer_start(&SI_info->sib3_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib4:
SI_info->sib4_validity = true;
nr_timer_start(&SI_info->sib4_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib5:
SI_info->sib5_validity = true;
nr_timer_start(&SI_info->sib5_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib6:
SI_info->sib6_validity = true;
nr_timer_start(&SI_info->sib6_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib7:
SI_info->sib7_validity = true;
nr_timer_start(&SI_info->sib7_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib8:
SI_info->sib8_validity = true;
nr_timer_start(&SI_info->sib8_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib9:
SI_info->sib9_validity = true;
nr_timer_start(&SI_info->sib9_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib10_v1610:
SI_info->sib10_validity = true;
nr_timer_start(&SI_info->sib10_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib11_v1610:
SI_info->sib11_validity = true;
nr_timer_start(&SI_info->sib11_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib12_v1610:
SI_info->sib12_validity = true;
nr_timer_start(&SI_info->sib12_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib13_v1610:
SI_info->sib13_validity = true;
nr_timer_start(&SI_info->sib13_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib14_v1610:
SI_info->sib14_validity = true;
nr_timer_start(&SI_info->sib14_timer);
break;
case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib19_v1700:
sib19 = typeandinfo->choice.sib19_v1700;
if (g_log->log_component[NR_RRC].level >= OAILOG_DEBUG)
xer_fprint(stdout, &asn_DEF_NR_SIB19_r17, (const void *)sib19);
nr_rrc_process_ntnconfig(rrc, SI_info, sib19->ntn_Config_r17, frame, false);
break;
default:
break;
}
}
if (sib19) {
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_OTHER_SIB;
nr_mac_rrc_config_other_sib_t *sib19_msg = &rrc_msg.payload.config_other_sib;
asn_copy(&asn_DEF_NR_SIB19_r17, (void **)&sib19_msg->sib19, sib19);
sib19_msg->hfn = hfn;
sib19_msg->frame = frame;
sib19_msg->can_start_ra = rrc->is_NTN_UE;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
}
static void nr_rrc_ue_prepare_RRCSetupRequest(NR_UE_RRC_INST_t *rrc)
{
LOG_D(NR_RRC, "Generation of RRCSetupRequest\n");
uint8_t rv[6];
// Get RRCConnectionRequest, fill random for now
// Generate random byte stream for contention resolution
for (int i = 0; i < 6; i++) {
#ifdef SMBV
// if SMBV is configured the contention resolution needs to be fix for the connection procedure to succeed
rv[i] = i;
#else
rv[i] = taus() & 0xff;
#endif
}
uint8_t buf[1024];
int len = do_RRCSetupRequest(buf, sizeof(buf), rv, rrc->fiveG_S_TMSI);
nr_rlc_srb_recv_sdu(rrc->ue_id, 0, buf, len);
}
static void nr_rrc_configure_default_SI(NR_UE_RRC_SI_INFO *SI_info,
NR_SI_SchedulingInfo_t *si_SchedulingInfo,
NR_SI_SchedulingInfo_v1700_t *si_SchedulingInfo_v1700)
{
for (int i = 0; i < MAX_SI_GROUPS; i++)
SI_info->default_otherSI_map[i] = 0;
int nb_groups = 0;
if (si_SchedulingInfo) {
nb_groups = si_SchedulingInfo->schedulingInfoList.list.count;
AssertFatal(nb_groups <= MAX_SI_GROUPS, "Exceeding max number of SI groups configured\n");
for (int i = 0; i < nb_groups; i++) {
NR_SchedulingInfo_t *schedulingInfo = si_SchedulingInfo->schedulingInfoList.list.array[i];
for (int j = 0; j < schedulingInfo->sib_MappingInfo.list.count; j++) {
NR_SIB_TypeInfo_t *sib_Type = schedulingInfo->sib_MappingInfo.list.array[j];
SI_info->default_otherSI_map[i] |= 1 << sib_Type->type;
}
}
}
if (si_SchedulingInfo_v1700) {
int start_idx = nb_groups;
nb_groups += si_SchedulingInfo_v1700->schedulingInfoList2_r17.list.count;
AssertFatal(nb_groups <= MAX_SI_GROUPS, "Exceeding max number of SI groups configured\n");
for (int i = 0; i < si_SchedulingInfo_v1700->schedulingInfoList2_r17.list.count; i++) {
NR_SchedulingInfo2_r17_t *schedulingInfo2 = si_SchedulingInfo_v1700->schedulingInfoList2_r17.list.array[i];
for (int j = 0; j < schedulingInfo2->sib_MappingInfo_r17.list.count; j++) {
NR_SIB_TypeInfo_v1700_t *sib_TypeInfo_v1700 = schedulingInfo2->sib_MappingInfo_r17.list.array[j];
if (sib_TypeInfo_v1700->sibType_r17.present == NR_SIB_TypeInfo_v1700__sibType_r17_PR_type1_r17) {
SI_info->default_otherSI_map[start_idx + i] |= 1 << (sib_TypeInfo_v1700->sibType_r17.choice.type1_r17 + 13);
}
}
}
}
}
static bool verify_NTN_access(const NR_UE_RRC_SI_INFO *SI_info, const NR_SIB1_v1700_IEs_t *sib1_v1700)
{
// SIB1 indicates if NTN access is present in the cell
bool ntn_access = false;
if (sib1_v1700 && sib1_v1700->cellBarredNTN_r17
&& *sib1_v1700->cellBarredNTN_r17 == NR_SIB1_v1700_IEs__cellBarredNTN_r17_notBarred)
ntn_access = true;
uint32_t sib19_mask = 1 << (NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType19 + 13);
int sib19_present = false;
for (int i = 0; i < MAX_SI_GROUPS; i++) {
sib19_present = SI_info->default_otherSI_map[i] & sib19_mask;
if (sib19_present)
break;
}
AssertFatal(!ntn_access || sib19_present, "NTN cell, but SIB19 not configured.\n");
return ntn_access && sib19_present;
}
static void get_sib19_schedinfo(NR_UE_RRC_SI_INFO *SI_info, NR_SI_SchedulingInfo_v1700_t *si_SchedInfo_v1700)
{
// Find the SIB19 periodicity configured in the scheduling info
if (si_SchedInfo_v1700) {
int count_v17 = si_SchedInfo_v1700->schedulingInfoList2_r17.list.count;
for (int i = 0; i < count_v17; i++) {
struct NR_SchedulingInfo2_r17 *schedulingInfo2 = si_SchedInfo_v1700->schedulingInfoList2_r17.list.array[i];
for (int j = 0; j < schedulingInfo2->sib_MappingInfo_r17.list.count; j++) {
struct NR_SIB_TypeInfo_v1700 *sib_TypeInfo_v1700 = schedulingInfo2->sib_MappingInfo_r17.list.array[j];
if (sib_TypeInfo_v1700->sibType_r17.present == NR_SIB_TypeInfo_v1700__sibType_r17_PR_type1_r17) {
if (sib_TypeInfo_v1700->sibType_r17.choice.type1_r17 == NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType19) {
SI_info->sib19_periodicity = 8 << schedulingInfo2->si_Periodicity_r17;
SI_info->sib19_windowposition = schedulingInfo2->si_WindowPosition_r17;
return;
}
}
}
}
}
}
static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_SIB1_t *sib1)
{
if(g_log->log_component[NR_RRC].level >= OAILOG_DEBUG)
xer_fprint(stdout, &asn_DEF_NR_SIB1, (const void *) sib1);
LOG_A(NR_RRC, "SIB1 decoded\n");
plmn_id_t *plmn_id = malloc_or_fail(sizeof(plmn_id_t));
/* selected_plmn_identity is one-indexed */
AssertFatal(rrc->selected_plmn_identity > 0, "No PLMN selected");
/* only one PLMN info block is currently supported*/
NR_PLMN_Identity_t *plmn = sib1->cellAccessRelatedInfo.plmn_IdentityInfoList.list.array[0]
->plmn_IdentityList.list.array[rrc->selected_plmn_identity - 1];
/* Convert MCC */
plmn_id->mcc = (*plmn->mcc->list.array[0]) * 100 + (*plmn->mcc->list.array[1]) * 10 + (*plmn->mcc->list.array[2]);
plmn_id->mnc_digit_length = plmn->mnc.list.count;
/* Convert MNC (2 or 3 digits) */
if (plmn->mnc.list.count == 3) {
plmn_id->mnc = (*plmn->mnc.list.array[0]) * 100 + (*plmn->mnc.list.array[1]) * 10 + (*plmn->mnc.list.array[2]);
} else {
plmn_id->mnc = (*plmn->mnc.list.array[0]) * 10 + (*plmn->mnc.list.array[1]);
}
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
nas->sn_id = plmn_id;
nr_timer_start(&SI_info->sib1_timer);
SI_info->sib1_validity = true;
if (rrc->nrRrcState == RRC_STATE_IDLE_NR) {
rrc->ra_trigger = RRC_CONNECTION_SETUP;
}
NR_SIB1_v1700_IEs_t *sib1_v1700 = NULL;
NR_SI_SchedulingInfo_v1700_t *si_SchedInfo_v1700 = NULL;
if (sib1->nonCriticalExtension
&& sib1->nonCriticalExtension->nonCriticalExtension
&& sib1->nonCriticalExtension->nonCriticalExtension->nonCriticalExtension) {
sib1_v1700 = sib1->nonCriticalExtension->nonCriticalExtension->nonCriticalExtension;
si_SchedInfo_v1700 = sib1_v1700->si_SchedulingInfo_v1700;
}
AssertFatal(sib1->servingCellConfigCommon, "configuration issue in SIB1\n");
SI_info->scs = sib1->servingCellConfigCommon->downlinkConfigCommon.initialDownlinkBWP.genericParameters.subcarrierSpacing;
SI_info->si_windowlength = (sib1->si_SchedulingInfo) ? sib1->si_SchedulingInfo->si_WindowLength : 0;
// configure default SI
nr_rrc_configure_default_SI(SI_info, sib1->si_SchedulingInfo, si_SchedInfo_v1700);
rrc->is_NTN_UE = verify_NTN_access(SI_info, sib1_v1700);
if (rrc->is_NTN_UE)
get_sib19_schedinfo(SI_info, si_SchedInfo_v1700);
// configure timers and constant
nr_rrc_set_sib1_timers_and_constants(&rrc->timers_and_constants, sib1);
// RRC storage of SIB1 timers and constants (eg needed in re-establishment)
UPDATE_IE(rrc->timers_and_constants.sib1_TimersAndConstants, sib1->ue_TimersAndConstants, NR_UE_TimersAndConstants_t);
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_SIB1;
nr_mac_rrc_config_sib1_t *config_sib1 = &rrc_msg.payload.config_sib1;
config_sib1->sib1 = sib1;
config_sib1->can_start_ra = !rrc->is_NTN_UE;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
/** @brief Synchronize NH chain based on nextHopChainingCount
*
* This function synchronizes the NH parameter chain as specified in 3GPP TS 33.501 Annex A.10.
* Use cases: Master Key Update, RRC Reestablishment: The UE receives an NCC value that is
* different from the NCC associated with the currently active KgNB. The NH chain is synchronized
* by computing the function defined in Annex A.10 iteratively (and increasing the NCC value until
* it matches the NCC value received from the source ng-gNB).
*
* @param kamf K_AMF key from NAS
* @param kgnb Current KgNB key (input/output)
* @param nh Current NH parameter (input/output)
* @param nhcc Pointer to current nextHopChainingCount (input/output)
* @param target_ncc Target nextHopChainingCount value to synchronize to */
static void nr_sync_nh_chain(const uint8_t kamf[SECURITY_KEY_LEN],
uint8_t kgnb[SECURITY_KEY_LEN],
uint8_t nh[SECURITY_KEY_LEN],
uint64_t *nhcc,
const int8_t target_ncc)
{
// If the UE received an NCC value that was different from the NCC associated with the
// currently active KgNB, the UE shall first synchronize the locally kept NH
// parameter by computing the function defined in Annex A.10 iteratively (and increasing
// the NCC value until it matches the NCC value received from the source ng-gNB).
if (target_ncc <= *nhcc) {
LOG_W(NR_RRC, "Received NCC=%d is less than or equal to current nhcc=%ld (nothing to sync)\n", target_ncc, *nhcc);
return;
}
if (target_ncc > *nhcc) {
LOG_I(NR_RRC, "Synchronizing NH chain: current nhcc=%ld, target ncc=%d\n", *nhcc, target_ncc);
if (*nhcc == 0) {
// First derivation: derive KgNB from KAMF, then derive NH from KgNB (per TS 33.501 A.10)
// Note: For the first NH derivation, we use UL NAS COUNT = 0 to match the AMF's derivation
// during handover. The AMF derives the first NH using the UL NAS COUNT from the last
// successful NAS SMC, which is 0 for the initial derivation.
derive_kgnb(kamf, 0, kgnb);
nr_derive_nh(kamf, kgnb, nh);
*nhcc = 1;
}
// Following derivations: iterate from current nhcc to target_ncc
for (uint8_t i = *nhcc; i < target_ncc; i++) {
LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i);
nr_derive_nh(kamf, nh, nh);
}
*nhcc = target_ncc;
}
}
/** @brief Derive KNG-RAN* (KgNB*) using horizontal derivation
*
* This function derives KNG-RAN* from the currently active KgNB (horizontal derivation)
* as specified in 3GPP TS 33.501 Annex A.11 and A.12.
* Used when NCC values match (no NH synchronization needed).
*
* @param pci Physical Cell ID
* @param nr_arfcn_dl NR ARFCN-DL
* @param kgnb Current KgNB key (input/output)
*/
static void nr_derive_kgnb_horizontal(const uint16_t pci, const uint64_t nr_arfcn_dl, uint8_t kgnb[SECURITY_KEY_LEN])
{
// When the NCC values match, the UE shall derive the KNG-RAN* from the currently active
// KgNB and the target PCI and its frequency ARFCN-DL using the function defined in Annex A.11 and A.12.
LOG_D(NR_RRC, "Deriving KNG-RAN* using horizontal derivation (from current KgNB)\n");
nr_derive_key_ng_ran_star(pci, nr_arfcn_dl, kgnb, kgnb);
}
/** @brief Derive KNG-RAN* (KgNB*) using vertical derivation
*
* This function derives KNG-RAN* from the synchronized NH parameter (vertical derivation)
* as specified in 3GPP TS 33.501 Annex A.11 and A.12.
* Used after NH chain synchronization when NCC values differ.
*
* @param pci Physical Cell ID
* @param nr_arfcn_dl NR ARFCN-DL
* @param kgnb Current KgNB key (input/output)
* @param nh Synchronized NH parameter (input)
*/
static void nr_derive_kgnb_vertical(const uint16_t pci,
const uint64_t nr_arfcn_dl,
uint8_t kgnb[SECURITY_KEY_LEN],
const uint8_t nh[SECURITY_KEY_LEN])
{
// When the NCC values match (after synchronization), the UE shall compute the K NG-RAN *
// from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL
// using the function defined in Annex A.11 and A.12.
LOG_D(NR_RRC, "Deriving KNG-RAN* using vertical derivation (from synchronized NH)\n");
nr_derive_key_ng_ran_star(pci, nr_arfcn_dl, nh, kgnb);
}
/** @brief Update KgNB based on received nextHopChainingCount
*
* This function implements the common logic for updating KgNB based on received NCC value,
* as specified in 3GPP TS 33.501 6.9.2.3.4.
*
* Per 33.501 6.9.2.3.4 and Fig 6.9.2.1.1-1:
* - If NCC received == current NCC: use horizontal derivation (from currently active KgNB)
* This applies regardless of whether NCC is 0 or >0. For a fixed NCC level, multiple
* horizontal derivations can be done within that level.
* - If NCC received > current NCC: synchronize NH chain (Annex A.10), then use vertical
* derivation (from NH) to enter the new NCC level.
*
* Horizontal derivation = derive KNG-RAN* from currently active KgNB + (PCI, ARFCN-DL)
* Vertical derivation = derive KNG-RAN* from NH + (PCI, ARFCN-DL)
*
* @param rrc RRC instance pointer
* @param kamf K_AMF key from NAS
* @param received_ncc Received nextHopChainingCount value */
static void nr_update_kgnb_from_ncc(NR_UE_RRC_INST_t *rrc, const uint8_t kamf[SECURITY_KEY_LEN], int8_t received_ncc)
{
const uint64_t original_nhcc = rrc->nhcc;
if (received_ncc == original_nhcc) {
// NCC values match: use horizontal derivation from currently active KgNB
// Per TS 33.501 6.9.2.3.4: "derive the KNG-RAN* from the currently active KgNB"
// This applies regardless of NCC being 0 or >0. For a fixed NCC level, we stay
// within that level using horizontal derivations.
LOG_D(NR_RRC, "NCC values match (%d), using horizontal derivation\n", received_ncc);
nr_derive_kgnb_horizontal(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb);
} else if (received_ncc < original_nhcc) {
// Note: According to spec, NCC should only increase. If received_ncc < original_nhcc,
// this is an error condition, but we handle it gracefully.
LOG_W(NR_RRC,
"Received NCC=%d is less than current nhcc=%ld (unexpected per spec, NH chain should only increase)\n",
received_ncc,
original_nhcc);
} else {
// received_ncc > original_nhcc: synchronize NH chain first (per 33.501 A.10)
nr_sync_nh_chain(kamf, rrc->kgnb, rrc->nh, &rrc->nhcc, received_ncc);
// Store the received nextHopChainingCount value (per 38.331 5.3.7.5)
LOG_D(NR_RRC, "Synchronizing NH chain to target NCC %d\n", received_ncc);
rrc->nhcc = received_ncc;
// After synchronization, derive KNG-RAN* from synchronized NH (vertical derivation)
// per 33.501 6.9.2.3.4: "When the NCC values match, the UE shall compute the K NG-RAN *
// from the synchronized NH parameter"
nr_derive_kgnb_vertical(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->nh);
}
}
/** @brief AS security key update procedure (5.3.5.7 3GPP TS 38.331) */
void as_security_key_update(NR_UE_RRC_INST_t *rrc, NR_MasterKeyUpdate_t *mku)
{
if (mku->nas_Container) {
LOG_E(NR_RRC, "forward the nas-Container to the upper layers: not implemented yet\n");
}
if (mku->keySetChangeIndicator) {
LOG_E(NR_RRC, "derive or update the K gNB key based on the K AMF key, as specified in TS 33.501: not implemented yet\n");
} else {
LOG_I(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount);
/** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized
* with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig
* processing that triggers bearer modifications. Security configueration of bearers must
* complete using the newly derived keys. As a workaround NAS is directly accessed here. */
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
const uint8_t *kamf = nas->security.kamf;
// KgNB update (TS 33.501 §6.9.2.3.3):
// If received NCC != local NCC, iteratively derive NH (Annex A.10) until NCC matches.
// Then derive the new KgNB from the synchronized NH.
nr_update_kgnb_from_ncc(rrc, kamf, mku->nextHopChainingCount);
}
}
static nr_pdcp_entity_security_keys_and_algos_t get_security_rrc_parameters(NR_UE_RRC_INST_t *ue, bool cp)
{
nr_pdcp_entity_security_keys_and_algos_t out = {0};
out.ciphering_algorithm = ue->cipheringAlgorithm;
out.integrity_algorithm = ue->integrityProtAlgorithm;
nr_derive_key(cp ? RRC_ENC_ALG : UP_ENC_ALG, ue->cipheringAlgorithm, ue->kgnb, out.ciphering_key);
nr_derive_key(cp ? RRC_INT_ALG : UP_INT_ALG, ue->integrityProtAlgorithm, ue->kgnb, out.integrity_key);
return out;
}
/** @brief Check if there is dedicated NAS information to forward to NAS */
static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530)
{
if (rec_1530->dedicatedNAS_MessageList) {
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *tmp = rec_1530->dedicatedNAS_MessageList;
for (int i = 0; i < tmp->list.count; i++) {
MessageDef *ittiMsg = itti_alloc_new_message(TASK_RRC_NRUE, rrc->ue_id, NAS_CONN_ESTABLI_CNF);
nas_establish_cnf_t *msg = &NAS_CONN_ESTABLI_CNF(ittiMsg);
msg->errCode = AS_SUCCESS;
msg->nasMsg.length = tmp->list.array[i]->size;
msg->nasMsg.nas_data = malloc_or_fail(msg->nasMsg.length);
memcpy(msg->nasMsg.nas_data, tmp->list.array[i]->buf, msg->nasMsg.length);
itti_send_msg_to_task(TASK_NAS_NRUE, rrc->ue_id, ittiMsg);
}
}
}
/** @brief Add bearer in PDCP/SDAP for 5GC association (SA) */
static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, const nr_pdcp_entity_security_keys_and_algos_t *sp)
{
DevAssert(drb->cnAssociation);
DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING);
// get SDAP config
sdap_config_t sdap = {0};
if (drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity) {
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity);
// EPC association
sdap.pdusession_id = drb->cnAssociation->choice.eps_BearerIdentity;
sdap.drb_id = drb->drb_Identity;
sdap.defaultDRB = true;
} else {
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config);
DevAssert(drb->cnAssociation->choice.sdap_Config);
sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity);
}
// add SDAP entity
nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, &sdap);
// add PDCP entity
nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp);
}
/**
* @brief add, modify and release SRBs and/or DRBs
* @ref 3GPP TS 38.331
*/
static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, NR_RadioBearerConfig_t *const radioBearerConfig)
{
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)radioBearerConfig);
if (radioBearerConfig->srb3_ToRelease) {
nr_pdcp_release_srb(ue_rrc->ue_id, 3);
ue_rrc->Srb[3] = RB_NOT_PRESENT;
}
nr_pdcp_entity_security_keys_and_algos_t security_rrc_parameters = {0};
nr_pdcp_entity_security_keys_and_algos_t security_up_parameters = {0};
if (ue_rrc->as_security_activated) {
if (radioBearerConfig->securityConfig != NULL) {
// When the field is not included, continue to use the currently configured keyToUse
if (radioBearerConfig->securityConfig->keyToUse) {
AssertFatal(*radioBearerConfig->securityConfig->keyToUse == NR_SecurityConfig__keyToUse_master,
"Secondary key usage seems not to be implemented\n");
ue_rrc->keyToUse = *radioBearerConfig->securityConfig->keyToUse;
}
// When the field is not included, continue to use the currently configured security algorithm
if (radioBearerConfig->securityConfig->securityAlgorithmConfig) {
ue_rrc->cipheringAlgorithm = radioBearerConfig->securityConfig->securityAlgorithmConfig->cipheringAlgorithm;
ue_rrc->integrityProtAlgorithm = *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm;
}
}
security_rrc_parameters = get_security_rrc_parameters(ue_rrc, true);
security_up_parameters = get_security_rrc_parameters(ue_rrc, false);
}
if (radioBearerConfig->srb_ToAddModList != NULL) {
for (int cnt = 0; cnt < radioBearerConfig->srb_ToAddModList->list.count; cnt++) {
struct NR_SRB_ToAddMod *srb = radioBearerConfig->srb_ToAddModList->list.array[cnt];
if (ue_rrc->Srb[srb->srb_Identity] == RB_NOT_PRESENT) {
ue_rrc->Srb[srb->srb_Identity] = RB_ESTABLISHED;
add_srb(false,
ue_rrc->ue_id,
radioBearerConfig->srb_ToAddModList->list.array[cnt],
&security_rrc_parameters);
}
else {
AssertFatal(srb->discardOnPDCP == NULL, "discardOnPDCP not yet implemented\n");
if (srb->reestablishPDCP) {
ue_rrc->Srb[srb->srb_Identity] = RB_ESTABLISHED;
nr_pdcp_reestablishment(ue_rrc->ue_id,
srb->srb_Identity,
true,
&security_rrc_parameters);
}
if (srb->pdcp_Config && srb->pdcp_Config->t_Reordering)
nr_pdcp_reconfigure_srb(ue_rrc->ue_id, srb->srb_Identity, *srb->pdcp_Config->t_Reordering);
}
}
}
if (radioBearerConfig->drb_ToReleaseList) {
for (int cnt = 0; cnt < radioBearerConfig->drb_ToReleaseList->list.count; cnt++) {
NR_DRB_Identity_t *DRB_id = radioBearerConfig->drb_ToReleaseList->list.array[cnt];
if (DRB_id) {
nr_pdcp_release_drb(ue_rrc->ue_id, *DRB_id);
set_DRB_status(ue_rrc, *DRB_id, RB_NOT_PRESENT);
}
}
}
/**
* Establish/reconfig DRBs if DRB-ToAddMod is present
* according to 3GPP TS 38.331 clause 5.3.5.6.5 DRB addition/modification
*/
if (radioBearerConfig->drb_ToAddModList != NULL) {
for (int cnt = 0; cnt < radioBearerConfig->drb_ToAddModList->list.count; cnt++) {
struct NR_DRB_ToAddMod *drb = radioBearerConfig->drb_ToAddModList->list.array[cnt];
int DRB_id = drb->drb_Identity;
if (get_DRB_status(ue_rrc, DRB_id) != RB_NOT_PRESENT) {
if (drb->reestablishPDCP) {
set_DRB_status(ue_rrc, DRB_id, RB_ESTABLISHED);
/* get integrity and cipehring settings from radioBearerConfig */
bool has_integrity = drb->pdcp_Config != NULL
&& drb->pdcp_Config->drb != NULL
&& drb->pdcp_Config->drb->integrityProtection != NULL;
bool has_ciphering = !(drb->pdcp_Config != NULL
&& drb->pdcp_Config->ext1 != NULL
&& drb->pdcp_Config->ext1->cipheringDisabled != NULL);
security_up_parameters.ciphering_algorithm = has_ciphering ? ue_rrc->cipheringAlgorithm : 0;
security_up_parameters.integrity_algorithm = has_integrity ? ue_rrc->integrityProtAlgorithm : 0;
/* re-establish */
nr_pdcp_reestablishment(ue_rrc->ue_id,
DRB_id,
false,
&security_up_parameters);
}
AssertFatal(drb->recoverPDCP == NULL, "recoverPDCP not yet implemented\n");
/* sdap-Config is included (SA mode) */
NR_SDAP_Config_t *sdap_Config = drb->cnAssociation ? drb->cnAssociation->choice.sdap_Config : NULL;
/* PDCP reconfiguration */
if (drb->pdcp_Config)
nr_pdcp_reconfigure_drb(ue_rrc->ue_id, DRB_id, drb->pdcp_Config);
/* SDAP entity reconfiguration */
if (sdap_Config)
nr_reconfigure_sdap_entity(sdap_Config, ue_rrc->ue_id, sdap_Config->pdu_Session, DRB_id);
} else {
set_DRB_status(ue_rrc ,DRB_id, RB_ESTABLISHED);
rrc_ue_add_bearer(ue_rrc->ue_id, radioBearerConfig->drb_ToAddModList->list.array[cnt], &security_up_parameters);
}
}
} // drb_ToAddModList //
ue_rrc->nrRrcState = RRC_STATE_CONNECTED_NR;
LOG_I(NR_RRC, "State = NR_RRC_CONNECTED\n");
}
static void nr_rrc_signal_maxrtxindication(int ue_id)
{
MessageDef *msg = itti_alloc_new_message(TASK_RLC_UE, ue_id, NR_RRC_RLC_MAXRTX);
NR_RRC_RLC_MAXRTX(msg).ue_id = ue_id;
itti_send_msg_to_task(TASK_RRC_NRUE, ue_id, msg);
}
/** @brief Release all active RLC entities for a UE and set to inactive.
* This is typically used when tearing down all DRBs at UE side,
* such as after a PDU session release or full reconfiguration.
* @param rrc Pointer to NR_UE_RRC_INST_t structure
* @param id Logical Channel ID (must be in range [0, NR_MAX_NUM_LCID-1]) */
static void nr_rrc_release_rlc_entity(NR_UE_RRC_INST_t *rrc, int id)
{
DevAssert(rrc);
DevAssert(id >= 0 && id < NR_MAX_NUM_LCID);
if (rrc->active_RLC_entity[id]) {
rrc->active_RLC_entity[id] = false;
nr_rlc_release_entity(rrc->ue_id, id);
LOG_I(RLC, "Released RLC entity: ue_id=%ld, lc_id=%d\n", rrc->ue_id, id);
}
}
static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupConfig_t *cellGroupConfig)
{
if (cellGroupConfig->rlc_BearerToReleaseList != NULL) {
for (int i = 0; i < cellGroupConfig->rlc_BearerToReleaseList->list.count; i++) {
NR_LogicalChannelIdentity_t *lcid = cellGroupConfig->rlc_BearerToReleaseList->list.array[i];
AssertFatal(lcid, "LogicalChannelIdentity shouldn't be null here\n");
nr_rrc_release_rlc_entity(rrc, *lcid);
}
}
if (cellGroupConfig->rlc_BearerToAddModList != NULL) {
for (int i = 0; i < cellGroupConfig->rlc_BearerToAddModList->list.count; i++) {
NR_RLC_BearerConfig_t *rlc_bearer = cellGroupConfig->rlc_BearerToAddModList->list.array[i];
NR_LogicalChannelIdentity_t lcid = rlc_bearer->logicalChannelIdentity;
if (rrc->active_RLC_entity[lcid]) {
if (rlc_bearer->reestablishRLC)
nr_rlc_reestablish_entity(rrc->ue_id, lcid);
if (rlc_bearer->rlc_Config)
nr_rlc_reconfigure_entity(rrc->ue_id, lcid, rlc_bearer->rlc_Config);
} else {
rrc->active_RLC_entity[lcid] = true;
AssertFatal(rlc_bearer->servedRadioBearer, "servedRadioBearer mandatory in case of setup\n");
AssertFatal(rlc_bearer->servedRadioBearer->present != NR_RLC_BearerConfig__servedRadioBearer_PR_NOTHING,
"Invalid RB for RLC configuration\n");
if (rlc_bearer->servedRadioBearer->present == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity) {
NR_SRB_Identity_t srb_id = rlc_bearer->servedRadioBearer->choice.srb_Identity;
nr_rlc_add_srb(rrc->ue_id, srb_id, rlc_bearer);
nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication);
} else { // DRB
NR_DRB_Identity_t drb_id = rlc_bearer->servedRadioBearer->choice.drb_Identity;
if (!rlc_bearer->rlc_Config) {
LOG_E(RLC, "RLC-Config not present but is mandatory for setup\n");
rrc->active_RLC_entity[lcid] = false;
} else {
nr_rlc_add_drb(rrc->ue_id, drb_id, rlc_bearer);
nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication);
}
}
}
}
}
}
static void nr_ue_meas_reset(meas_t *meas_cell, bool csi_meas)
{
if (csi_meas)
meas_cell->csi_rsrp_dBm.init = false;
else
meas_cell->ss_rsrp_dBm.init = false;
}
static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc,
NR_ReconfigurationWithSync_t *reconfigurationWithSync,
int gNB_index)
{
// perform Reconfiguration with sync according to 5.3.5.5.2
if (!rrc->as_security_activated && !(get_softmodem_params()->phy_test || get_softmodem_params()->do_ra)) {
// if the AS security is not activated, perform the actions upon going to RRC_IDLE as specified in 5.3.11
// with the release cause 'other' upon which the procedure ends
NR_Release_Cause_t release_cause = OTHER;
nr_rrc_going_to_IDLE(rrc, release_cause, NULL);
return;
}
// Clear neighbor cell lists from measurement objects during handover
rrcPerNB_t *rrcNB = &rrc->perNB[gNB_index];
for (int i = 0; i < MAX_MEAS_OBJ; i++) {
if (rrcNB->MeasObj[i] && rrcNB->MeasObj[i]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
NR_MeasObjectNR_t *measObjNR = rrcNB->MeasObj[i]->measObject.choice.measObjectNR;
if (measObjNR->cellsToAddModList) {
ASN_STRUCT_FREE(asn_DEF_NR_CellsToAddModList, measObjNR->cellsToAddModList);
measObjNR->cellsToAddModList = NULL;
}
}
}
l3_measurements_t *l3m = &rrcNB->l3_measurements;
for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) {
nr_ue_meas_reset(&l3m->neighboring_cell[i], false);
nr_ue_meas_reset(&l3m->neighboring_cell[i], true);
}
if (reconfigurationWithSync->spCellConfigCommon) {
/* if the frequencyInfoDL is included, consider the target SpCell
to be one on the SSB frequency indicated by the frequencyInfoDL */
const NR_DownlinkConfigCommon_t *dcc = reconfigurationWithSync->spCellConfigCommon->downlinkConfigCommon;
if (dcc && dcc->frequencyInfoDL && dcc->frequencyInfoDL->absoluteFrequencySSB) {
rrc->arfcn_ssb = *dcc->frequencyInfoDL->absoluteFrequencySSB;
LOG_I(NR_RRC, "UE %ld: updated ARFCN_SSB=%ld\n", rrc->ue_id, rrc->arfcn_ssb);
}
// consider the target SpCell to be one with a physical cell identity indicated by the physCellId
if (!reconfigurationWithSync->spCellConfigCommon->physCellId)
LOG_E(NR_RRC, "physCellId absent but should be mandatory present upon cell change and cell addition\n");
else
rrc->phyCellID = *reconfigurationWithSync->spCellConfigCommon->physCellId;
}
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
nr_timer_stop(&tac->T310);
if (!get_softmodem_params()->phy_test) {
// T304 is stopped upon completion of RA procedure which is not done in phy-test mode
int t304_value = nr_rrc_get_T304(reconfigurationWithSync->t304);
nr_timer_setup(&tac->T304, t304_value, 10); // 10ms step
nr_timer_start(&tac->T304);
}
rrc->rnti = reconfigurationWithSync->newUE_Identity;
// reset the MAC entity of this cell group (done at MAC in handle_reconfiguration_with_sync)
// 3GPP TS38.331 section 5.3.5.5.2
nr_timer_stop(&tac->T430);
if (rrc->target_ntncfg) {
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
rrc->target_ntncfg = NULL;
rrc->process_target_ntncfg = false;
}
if (reconfigurationWithSync->spCellConfigCommon &&
reconfigurationWithSync->spCellConfigCommon->ext2 &&
reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17) {
NR_NTN_Config_r17_t *ntncfg = reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17;
// EPOCH time is always sent if NTN config is sent through DCCH
AssertFatal(ntncfg->epochTime_r17, "NTN-CONFIG sent in dedicated mode should have EPOCHTIME\n");
const int copy_result = asn_copy(&asn_DEF_NR_NTN_Config_r17, (void **)&rrc->target_ntncfg, ntncfg);
AssertFatal(copy_result == 0, "unable to copy NR_NTN_Config_r17_t\n");
}
}
static bool nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupConfig_t *cgConfig, int gNB_index, bool dedicatedsib1)
{
if (!check_cellgroup_config(cgConfig)) {
// if we received a configuration not supported or with some wrong combination
// we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise)
handle_rlf_detection(rrc);
return false;
}
NR_SpCellConfig_t *spCellConfig = cgConfig->spCellConfig;
if(spCellConfig) {
NR_ServingCellConfig_t *spCellConfigDedicated = spCellConfig->spCellConfigDedicated;
if (spCellConfigDedicated) {
if (spCellConfigDedicated->firstActiveDownlinkBWP_Id)
rrc->dl_bwp_id = *spCellConfigDedicated->firstActiveDownlinkBWP_Id;
if (spCellConfigDedicated->uplinkConfig && spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)
rrc->ul_bwp_id = *spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id;
}
NR_ReconfigurationWithSync_t *reconfigurationWithSync = spCellConfig->reconfigurationWithSync;
if (reconfigurationWithSync) {
LOG_I(NR_RRC, "Processing reconfigurationWithSync\n");
nr_rrc_process_reconfigurationWithSync(rrc, reconfigurationWithSync, gNB_index);
// if RRCReconfiguration does not include dedicatedSIB1-Delivery
// if the active downlink BWP, which is indicated by the firstActiveDownlinkBWP-Id for the target SpCell of the MCG,
// has a common search space configured by searchSpaceSIB1
// acquire the SIB1, which is scheduled as specified in TS 38.213 [13], of the target SpCell of the MCG
if (!dedicatedsib1 && IS_SA_MODE(get_softmodem_params())) {
if (rrc->dl_bwp_id == 0) {
// Check initial DL BWP for searchSpaceSIB1
NR_ServingCellConfigCommon_t *spCellConfigCommon = reconfigurationWithSync->spCellConfigCommon;
if (spCellConfigCommon) {
NR_DownlinkConfigCommon_t *downlinkConfig = spCellConfigCommon->downlinkConfigCommon;
if (downlinkConfig
&& downlinkConfig->initialDownlinkBWP
&& downlinkConfig->initialDownlinkBWP->pdcch_ConfigCommon
&& downlinkConfig->initialDownlinkBWP->pdcch_ConfigCommon->present == NR_SetupRelease_PDCCH_ConfigCommon_PR_setup
&& downlinkConfig->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1) {
rrc->sched_reconfsync_sib1 = true;
}
}
} else {
// Check dedicated DL BWP for searchSpaceSIB1
if (spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) {
for (int i = 0; i < spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; i++) {
NR_BWP_Downlink_t *bwp = spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[i];
if (bwp->bwp_Id == rrc->dl_bwp_id && bwp->bwp_Common && bwp->bwp_Common->pdcch_ConfigCommon
&& bwp->bwp_Common->pdcch_ConfigCommon->present == NR_SetupRelease_PDCCH_ConfigCommon_PR_setup
&& bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1) {
rrc->sched_reconfsync_sib1 = true;
break;
}
}
}
}
}
}
nr_rrc_handle_SetupRelease_RLF_TimersAndConstants(rrc, spCellConfig->rlf_TimersAndConstants);
}
nr_rrc_manage_rlc_bearers(rrc, cgConfig);
return true;
}
static bool nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc,
OCTET_STRING_t *masterCellGroup,
long *fullConfig,
int gNB_index)
{
AssertFatal(!fullConfig, "fullConfig not supported yet\n");
NR_CellGroupConfig_t *cellGroupConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_CellGroupConfig, //might be added prefix later
(void **)&cellGroupConfig,
(uint8_t *)masterCellGroup->buf,
masterCellGroup->size, 0, 0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "CellGroupConfig decode error\n");
// if the ASN1 decoding fails for the received CellGroup configuration
// we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise)
handle_rlf_detection(rrc);
return false;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig);
}
bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, false);
if (!ret)
return false;
LOG_D(RRC, "Sending CellGroupConfig to MAC the pointer will be managed by mac\n");
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG;
nr_mac_rrc_config_cg_t *mac_msg = &rrc_msg.payload.config_cg;
mac_msg->cellGroupConfig = cellGroupConfig;
mac_msg->UE_NR_Capability = rrc->UECap.UE_NR_Capability;
mac_msg->hfn = rrc->current_hfn;
mac_msg->frame = rrc->current_frame;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
return true;
}
static bool nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
{
if (rec_1530->fullConfig) {
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
}
if (rec_1530->masterCellGroup) {
bool ret = nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
if (!ret)
return false;
}
if (rec_1530->masterKeyUpdate) {
as_security_key_update(rrc, rec_1530->masterKeyUpdate);
nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true);
nr_pdcp_config_set_security(rrc->ue_id, 1, true, &sp);
}
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
bool dedicatedsib1 = false;
if (rec_1530->dedicatedSIB1_Delivery) {
dedicatedsib1 = true;
NR_SIB1_t *sib1 = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_SIB1,
(void **)&sib1,
(uint8_t *)rec_1530->dedicatedSIB1_Delivery->buf,
rec_1530->dedicatedSIB1_Delivery->size,
0,
0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "dedicatedSIB1-Delivery decode error\n");
SEQUENCE_free(&asn_DEF_NR_SIB1, sib1, 1);
} else {
// mac layer will free sib1
nr_rrc_process_sib1(rrc, SI_info, sib1);
}
}
if (rec_1530->dedicatedSystemInformationDelivery) {
NR_SystemInformation_t *si = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_SystemInformation,
(void **)&si,
(uint8_t *)rec_1530->dedicatedSystemInformationDelivery->buf,
rec_1530->dedicatedSystemInformationDelivery->size,
0,
0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "dedicatedSystemInformationDelivery decode error\n");
SEQUENCE_free(&asn_DEF_NR_SystemInformation, si, 1);
} else {
LOG_I(NR_RRC, "[UE %ld] Decoding dedicatedSystemInformationDelivery\n", rrc->ue_id);
nr_decode_SI(SI_info, si, rrc, rrc->current_hfn, rrc->current_frame);
}
}
if (rec_1530->otherConfig) {
// TODO perform the other configuration procedure as specified in 5.3.5.9
LOG_E(NR_RRC, "RRCReconfiguration includes otherConfig but this is not handled yet\n");
}
NR_RRCReconfiguration_v1540_IEs_t *rec_1540 = rec_1530->nonCriticalExtension;
if (rec_1540) {
NR_RRCReconfiguration_v1560_IEs_t *rec_1560 = rec_1540->nonCriticalExtension;
if (rec_1560) {
if (rec_1560->sk_Counter) {
// TODO perform AS security key update procedure as specified in 5.3.5.7
LOG_E(NR_RRC, "RRCReconfiguration includes sk-Counter but this is not implemented yet\n");
}
if (rec_1560->mrdc_SecondaryCellGroupConfig) {
// TODO perform handling of mrdc-SecondaryCellGroupConfig as specified in 5.3.5.3
LOG_E(NR_RRC, "RRCReconfiguration includes mrdc-SecondaryCellGroupConfig but this is not handled yet\n");
}
if (rec_1560->radioBearerConfig2) {
NR_RadioBearerConfig_t *RadioBearerConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_RadioBearerConfig,
(void **)&RadioBearerConfig,
(uint8_t *)rec_1560->radioBearerConfig2->buf,
rec_1560->radioBearerConfig2->size,
0,
0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "radioBearerConfig2 decode error\n");
SEQUENCE_free(&asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1);
} else
nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig);
}
}
}
return dedicatedsib1;
}
static void handle_meas_reporting_remove(rrcPerNB_t *rrc, int id, NR_UE_Timers_Constants_t *timers)
{
// remove the measurement reporting entry for this measId if included
asn1cFreeStruc(asn_DEF_NR_VarMeasReport, rrc->MeasReport[id]);
// TODO stop the periodical reporting timer or timer T321, whichever is running,
// and reset the associated information (e.g. timeToTrigger) for this measId
nr_timer_stop(&timers->T321);
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
nr_timer_stop(&l3_measurements->TA2);
nr_timer_stop(&l3_measurements->periodic_report_timer);
l3_measurements->reports_sent = 0;
l3_measurements->max_reports = 0;
l3_measurements->report_interval_ms = 0;
}
static void handle_measobj_remove(rrcPerNB_t *rrc, struct NR_MeasObjectToRemoveList *remove_list, NR_UE_Timers_Constants_t *timers)
{
// section 5.5.2.4 in 38.331
for (int i = 0; i < remove_list->list.count; i++) {
// for each measObjectId included in the received measObjectToRemoveList
// that is part of measObjectList in the configuration
NR_MeasObjectId_t id = *remove_list->list.array[i];
if (rrc->MeasObj[id - 1]) {
// remove the entry with the matching measObjectId from the measObjectList
asn1cFreeStruc(asn_DEF_NR_MeasObjectToAddMod, rrc->MeasObj[id - 1]);
// remove all measId associated with this measObjectId from the measIdList
for (int j = 0; j < MAX_MEAS_ID; j++) {
if (rrc->MeasId[j] && rrc->MeasId[j]->measObjectId == id) {
asn1cFreeStruc(asn_DEF_NR_MeasIdToAddMod, rrc->MeasId[j]);
handle_meas_reporting_remove(rrc, j, timers);
}
}
}
}
}
static void update_ssb_configmob(NR_SSB_ConfigMobility_t *source, NR_SSB_ConfigMobility_t *target)
{
if (source->ssb_ToMeasure)
HANDLE_SETUPRELEASE_IE(target->ssb_ToMeasure, source->ssb_ToMeasure, NR_SSB_ToMeasure_t, asn_DEF_NR_SSB_ToMeasure);
target->deriveSSB_IndexFromCell = source->deriveSSB_IndexFromCell;
if (source->ss_RSSI_Measurement)
UPDATE_IE(target->ss_RSSI_Measurement, source->ss_RSSI_Measurement, NR_SS_RSSI_Measurement_t);
}
static void update_nr_measobj(NR_MeasObjectNR_t *source, NR_MeasObjectNR_t *target)
{
UPDATE_IE(target->ssbFrequency, source->ssbFrequency, NR_ARFCN_ValueNR_t);
UPDATE_IE(target->ssbSubcarrierSpacing, source->ssbSubcarrierSpacing, NR_SubcarrierSpacing_t);
UPDATE_IE(target->smtc1, source->smtc1, NR_SSB_MTC_t);
if (source->smtc2) {
target->smtc2->periodicity = source->smtc2->periodicity;
if (source->smtc2->pci_List)
UPDATE_IE(target->smtc2->pci_List, source->smtc2->pci_List, struct NR_SSB_MTC2__pci_List);
}
else
asn1cFreeStruc(asn_DEF_NR_SSB_MTC2, target->smtc2);
UPDATE_IE(target->refFreqCSI_RS, source->refFreqCSI_RS, NR_ARFCN_ValueNR_t);
if (source->referenceSignalConfig.ssb_ConfigMobility)
update_ssb_configmob(source->referenceSignalConfig.ssb_ConfigMobility, target->referenceSignalConfig.ssb_ConfigMobility);
UPDATE_IE(target->absThreshSS_BlocksConsolidation, source->absThreshSS_BlocksConsolidation, NR_ThresholdNR_t);
UPDATE_IE(target->absThreshCSI_RS_Consolidation, source->absThreshCSI_RS_Consolidation, NR_ThresholdNR_t);
UPDATE_IE(target->nrofSS_BlocksToAverage, source->nrofSS_BlocksToAverage, long);
UPDATE_IE(target->nrofCSI_RS_ResourcesToAverage, source->nrofCSI_RS_ResourcesToAverage, long);
target->quantityConfigIndex = source->quantityConfigIndex;
target->offsetMO = source->offsetMO;
if (source->cellsToRemoveList) {
RELEASE_IE_FROMLIST(source->cellsToRemoveList, target->cellsToAddModList, physCellId);
}
if (source->cellsToAddModList) {
if (!target->cellsToAddModList)
target->cellsToAddModList = calloc(1, sizeof(*target->cellsToAddModList));
ADDMOD_IE_FROMLIST(source->cellsToAddModList, target->cellsToAddModList, physCellId, NR_CellsToAddMod_t);
}
if (source->excludedCellsToRemoveList) {
RELEASE_IE_FROMLIST(source->excludedCellsToRemoveList, target->excludedCellsToAddModList, pci_RangeIndex);
}
if (source->excludedCellsToAddModList) {
if (!target->excludedCellsToAddModList)
target->excludedCellsToAddModList = calloc(1, sizeof(*target->excludedCellsToAddModList));
ADDMOD_IE_FROMLIST(source->excludedCellsToAddModList, target->excludedCellsToAddModList, pci_RangeIndex, NR_PCI_RangeElement_t);
}
if (source->allowedCellsToRemoveList) {
RELEASE_IE_FROMLIST(source->allowedCellsToRemoveList, target->allowedCellsToAddModList, pci_RangeIndex);
}
if (source->allowedCellsToAddModList) {
if (!target->allowedCellsToAddModList)
target->allowedCellsToAddModList = calloc(1, sizeof(*target->allowedCellsToAddModList));
ADDMOD_IE_FROMLIST(source->allowedCellsToAddModList, target->allowedCellsToAddModList, pci_RangeIndex, NR_PCI_RangeElement_t);
}
if (source->ext1) {
UPDATE_IE(target->ext1->freqBandIndicatorNR, source->ext1->freqBandIndicatorNR, NR_FreqBandIndicatorNR_t);
UPDATE_IE(target->ext1->measCycleSCell, source->ext1->measCycleSCell, long);
}
}
static void handle_measobj_addmod(rrcPerNB_t *rrc, struct NR_MeasObjectToAddModList *addmod_list)
{
// section 5.5.2.5 in 38.331
for (int i = 0; i < addmod_list->list.count; i++) {
NR_MeasObjectToAddMod_t *measObj = addmod_list->list.array[i];
if (measObj->measObject.present != NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
LOG_E(NR_RRC, "Cannot handle MeasObjt other than NR\n");
continue;
}
NR_MeasObjectId_t id = measObj->measObjectId;
if (rrc->MeasObj[id]) {
update_nr_measobj(measObj->measObject.choice.measObjectNR, rrc->MeasObj[id]->measObject.choice.measObjectNR);
}
else {
// add a new entry for the received measObject to the measObjectList
UPDATE_IE(rrc->MeasObj[id], addmod_list->list.array[i], NR_MeasObjectToAddMod_t);
}
}
}
static void handle_reportconfig_remove(rrcPerNB_t *rrc,
struct NR_ReportConfigToRemoveList *remove_list,
NR_UE_Timers_Constants_t *timers)
{
for (int i = 0; i < remove_list->list.count; i++) {
NR_ReportConfigId_t id = *remove_list->list.array[i];
// remove the entry with the matching reportConfigId from the reportConfigList
asn1cFreeStruc(asn_DEF_NR_ReportConfigToAddMod, rrc->ReportConfig[id]);
for (int j = 0; j < MAX_MEAS_ID; j++) {
if (rrc->MeasId[j] && rrc->MeasId[j]->reportConfigId == id) {
// remove all measId associated with the reportConfigId from the measIdList
asn1cFreeStruc(asn_DEF_NR_MeasIdToAddMod, rrc->MeasId[j]);
handle_meas_reporting_remove(rrc, j, timers);
}
}
}
}
static void handle_reportconfig_addmod(rrcPerNB_t *rrc,
struct NR_ReportConfigToAddModList *addmod_list,
NR_UE_Timers_Constants_t *timers)
{
for (int i = 0; i < addmod_list->list.count; i++) {
NR_ReportConfigToAddMod_t *rep = addmod_list->list.array[i];
if (rep->reportConfig.present != NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR) {
LOG_E(NR_RRC, "Cannot handle reportConfig type other than NR\n");
continue;
}
NR_ReportConfigId_t id = rep->reportConfigId;
if (rrc->ReportConfig[id]) {
for (int j = 0; j < MAX_MEAS_ID; j++) {
// for each measId associated with this reportConfigId included in the measIdList
if (rrc->MeasId[j] && rrc->MeasId[j]->reportConfigId == id)
handle_meas_reporting_remove(rrc, j, timers);
}
}
UPDATE_IE(rrc->ReportConfig[id], addmod_list->list.array[i], NR_ReportConfigToAddMod_t);
}
}
static void handle_quantityconfig(rrcPerNB_t *rrc, NR_QuantityConfig_t *quantityConfig, NR_UE_Timers_Constants_t *timers)
{
if (quantityConfig->quantityConfigNR_List) {
for (int i = 0; i < quantityConfig->quantityConfigNR_List->list.count; i++) {
NR_QuantityConfigNR_t *quantityNR = quantityConfig->quantityConfigNR_List->list.array[i];
if (!rrc->QuantityConfig[i])
rrc->QuantityConfig[i] = calloc(1, sizeof(*rrc->QuantityConfig[i]));
rrc->QuantityConfig[i]->quantityConfigCell = quantityNR->quantityConfigCell;
// TODO: It remains to compute ssb_filter_coeff_rsrp and csi_RS_filter_coeff_rsrp for multiple quantityConfig
// TS 38.331 - 5.5.3.2 Layer 3 filtering
NR_QuantityConfigRS_t *qcc = &quantityNR->quantityConfigCell;
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
if (qcc->ssb_FilterConfig.filterCoefficientRSRP)
l3_measurements->ssb_filter_coeff_rsrp = 1. / pow(2, (*qcc->ssb_FilterConfig.filterCoefficientRSRP) / 4);
if (qcc->csi_RS_FilterConfig.filterCoefficientRSRP)
l3_measurements->csi_RS_filter_coeff_rsrp = 1. / pow(2, (*qcc->csi_RS_FilterConfig.filterCoefficientRSRP) / 4);
if (quantityNR->quantityConfigRS_Index)
UPDATE_IE(rrc->QuantityConfig[i]->quantityConfigRS_Index, quantityNR->quantityConfigRS_Index, struct NR_QuantityConfigRS);
}
}
for (int j = 0; j < MAX_MEAS_ID; j++) {
// for each measId included in the measIdList
if (rrc->MeasId[j])
handle_meas_reporting_remove(rrc, j, timers);
}
}
static void handle_measid_remove(rrcPerNB_t *rrc, struct NR_MeasIdToRemoveList *remove_list, NR_UE_Timers_Constants_t *timers)
{
for (int i = 0; i < remove_list->list.count; i++) {
NR_MeasId_t id = *remove_list->list.array[i];
if (rrc->MeasId[id]) {
asn1cFreeStruc(asn_DEF_NR_MeasIdToAddMod, rrc->MeasId[id]);
handle_meas_reporting_remove(rrc, id, timers);
}
}
}
static void handle_measid_addmod(rrcPerNB_t *rrc,
struct NR_MeasIdToAddModList *addmod_list,
NR_UE_Timers_Constants_t *timers,
nr_neighbor_cell_info_t *neighbor_cells,
int *num_neighbors)
{
for (int i = 0; i < addmod_list->list.count; i++) {
NR_MeasId_t id = addmod_list->list.array[i]->measId;
NR_ReportConfigId_t reportId = addmod_list->list.array[i]->reportConfigId;
NR_MeasObjectId_t measObjectId = addmod_list->list.array[i]->measObjectId;
UPDATE_IE(rrc->MeasId[id], addmod_list->list.array[i], NR_MeasIdToAddMod_t);
handle_meas_reporting_remove(rrc, id, timers);
if (rrc->ReportConfig[reportId]) {
NR_ReportConfigToAddMod_t *report = rrc->ReportConfig[reportId];
AssertFatal(report->reportConfig.present == NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR,
"Only NR config report is supported\n");
NR_ReportConfigNR_t *reportNR = report->reportConfig.choice.reportConfigNR;
// if the reportType is set to reportCGI in the reportConfig associated with this measId
if (reportNR->reportType.present == NR_ReportConfigNR__reportType_PR_reportCGI) {
if (rrc->MeasObj[measObjectId]) {
if (rrc->MeasObj[measObjectId]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
NR_MeasObjectNR_t *obj_nr = rrc->MeasObj[measObjectId]->measObject.choice.measObjectNR;
NR_ARFCN_ValueNR_t freq = 0;
if (obj_nr->ssbFrequency)
freq = *obj_nr->ssbFrequency;
else if (obj_nr->refFreqCSI_RS)
freq = *obj_nr->refFreqCSI_RS;
AssertFatal(freq > 0, "Invalid ARFCN frequency for this measurement object\n");
if (get_freq_range_from_arfcn(freq) == FR2)
nr_timer_setup(&timers->T321, 16000, 10); // 16 seconds for FR2
else
nr_timer_setup(&timers->T321, 2000, 10); // 2 seconds for FR1
}
else // EUTRA
nr_timer_setup(&timers->T321, 1000, 10); // 1 second for EUTRA
nr_timer_start(&timers->T321);
}
}
// Check for A3 event and extract neighbor cell info for MAC/PHY
else if (reportNR->reportType.present == NR_ReportConfigNR__reportType_PR_eventTriggered) {
NR_EventTriggerConfig_t *eventTriggerConfig = reportNR->reportType.choice.eventTriggered;
if (eventTriggerConfig->eventId.present == NR_EventTriggerConfig__eventId_PR_eventA3) {
if (rrc->MeasObj[measObjectId]
&& rrc->MeasObj[measObjectId]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
NR_MeasObjectNR_t *obj_nr = rrc->MeasObj[measObjectId]->measObject.choice.measObjectNR;
uint32_t ssb_freq = 0;
if (obj_nr->ssbFrequency) {
ssb_freq = *obj_nr->ssbFrequency;
}
if (obj_nr->cellsToAddModList) {
NR_CellsToAddModList_t *cellsToAddModList = obj_nr->cellsToAddModList;
for (int j = 0; j < cellsToAddModList->list.count; j++) {
if (*num_neighbors < NUMBER_OF_NEIGHBORING_CELLS_MAX) {
NR_CellsToAddMod_t *cell = cellsToAddModList->list.array[j];
neighbor_cells[*num_neighbors].Nid_cell = cell->physCellId;
neighbor_cells[*num_neighbors].ssb_freq = ssb_freq;
neighbor_cells[*num_neighbors].active = 1;
(*num_neighbors)++;
} else {
LOG_W(NR_RRC,
"More than %d neighboring cells configured, ignoring excess cells!\n",
NUMBER_OF_NEIGHBORING_CELLS_MAX);
break;
}
}
} else {
// We do not know PCI of neighboring cells, so we do blind search
if (*num_neighbors < NUMBER_OF_NEIGHBORING_CELLS_MAX) {
neighbor_cells[*num_neighbors].Nid_cell = -1;
neighbor_cells[*num_neighbors].ssb_freq = ssb_freq;
neighbor_cells[*num_neighbors].active = 1;
(*num_neighbors)++;
}
}
}
}
}
}
}
}
static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc,
NR_MeasConfig_t *const measConfig,
NR_UE_Timers_Constants_t *timers,
nr_neighbor_cell_info_t *neighbor_cells,
int *num_neighbors)
{
if (measConfig->measObjectToRemoveList)
handle_measobj_remove(rrc, measConfig->measObjectToRemoveList, timers);
if (measConfig->measObjectToAddModList)
handle_measobj_addmod(rrc, measConfig->measObjectToAddModList);
if (measConfig->reportConfigToRemoveList)
handle_reportconfig_remove(rrc, measConfig->reportConfigToRemoveList, timers);
if (measConfig->reportConfigToAddModList)
handle_reportconfig_addmod(rrc, measConfig->reportConfigToAddModList, timers);
if (measConfig->quantityConfig)
handle_quantityconfig(rrc, measConfig->quantityConfig, timers);
if (measConfig->measIdToRemoveList)
handle_measid_remove(rrc, measConfig->measIdToRemoveList, timers);
if (measConfig->measIdToAddModList)
handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers, neighbor_cells, num_neighbors);
LOG_W(NR_RRC, "Measurement gaps not yet supported!\n");
if (measConfig->s_MeasureConfig) {
if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) {
rrc->s_measure = measConfig->s_MeasureConfig->choice.ssb_RSRP;
} else if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_csi_RSRP) {
rrc->s_measure = measConfig->s_MeasureConfig->choice.csi_RSRP;
}
}
}
static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_index, NR_RRCReconfiguration_t *reconfiguration)
{
rrcPerNB_t *rrcNB = rrc->perNB + gNB_index;
switch (reconfiguration->criticalExtensions.present) {
case NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration: {
NR_RRCReconfiguration_IEs_t *ie = reconfiguration->criticalExtensions.choice.rrcReconfiguration;
bool dedicatedsib1 = false;
if (ie->nonCriticalExtension)
dedicatedsib1 = nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
if (ie->radioBearerConfig) {
LOG_I(NR_RRC, "RRCReconfiguration includes radio Bearer Configuration\n");
nr_rrc_ue_process_RadioBearerConfig(rrc, ie->radioBearerConfig);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)ie->radioBearerConfig);
}
/** @note This triggers PDU Session Establishment Accept which sets up the TUN interface.
* SDAP entity is a pre-requisite, therefore the radioBearerConfig has to be processed
* (in nr_rrc_ue_process_RadioBearerConfig, add_drb) early enough, or it may cause a race condition in SDAP. */
if (ie->nonCriticalExtension)
nr_rrc_process_dedicatedNAS_MessageList(rrc, ie->nonCriticalExtension);
if (ie->secondaryCellGroup) {
NR_CellGroupConfig_t *cellGroupConfig = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_CellGroupConfig, // might be added prefix later
(void **)&cellGroupConfig,
(uint8_t *)ie->secondaryCellGroup->buf,
ie->secondaryCellGroup->size,
0,
0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
uint8_t *buffer = ie->secondaryCellGroup->buf;
LOG_E(NR_RRC, "NR_CellGroupConfig decode error\n");
for (int i = 0; i < ie->secondaryCellGroup->size; i++)
LOG_E(NR_RRC, "%02x ", buffer[i]);
LOG_E(NR_RRC, "\n");
// free the memory
SEQUENCE_free(&asn_DEF_NR_CellGroupConfig, (void *)cellGroupConfig, 1);
// if the ASN1 decoding fails for the received CellGroup configuration
// we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise)
handle_rlf_detection(rrc);
} else {
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig);
bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, dedicatedsib1);
AssertFatal(ret, "CellGroup has wrong configuration for the UE. Unexpected\n");
AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n");
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG;
nr_mac_rrc_config_cg_t *config_cg = &rrc_msg.payload.config_cg;
config_cg->cellGroupConfig = cellGroupConfig;
config_cg->UE_NR_Capability = rrc->UECap.UE_NR_Capability;
config_cg->hfn = rrc->current_hfn;
config_cg->frame = rrc->current_frame;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
}
if (ie->measConfig) {
LOG_I(NR_RRC, "RRCReconfiguration includes Measurement Configuration\n");
nr_neighbor_cell_info_t neighbor_cells[NUMBER_OF_NEIGHBORING_CELLS_MAX];
int num_neighbors = 0;
nr_rrc_ue_process_measConfig(rrcNB, ie->measConfig, &rrc->timers_and_constants, neighbor_cells, &num_neighbors);
if (num_neighbors > 0) {
nr_rrc_mac_config_req_meas(rrc->ue_id, neighbor_cells, num_neighbors);
}
}
if (ie->lateNonCriticalExtension) {
LOG_E(NR_RRC, "RRCReconfiguration includes lateNonCriticalExtension. Not handled.\n");
}
} break;
case NR_RRCReconfiguration__criticalExtensions_PR_NOTHING:
case NR_RRCReconfiguration__criticalExtensions_PR_criticalExtensionsFuture:
default:
break;
}
return;
}
void process_nsa_message(NR_UE_RRC_INST_t *rrc, nsa_message_t nsa_message_type, void *message, int msg_len)
{
switch (nsa_message_type) {
case nr_SecondaryCellGroupConfig_r15: {
NR_RRCReconfiguration_t *RRCReconfiguration=NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_RRCReconfiguration,
(void **)&RRCReconfiguration,
(uint8_t *)message,
msg_len);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "NR_RRCReconfiguration decode error\n");
// free the memory
SEQUENCE_free(&asn_DEF_NR_RRCReconfiguration, RRCReconfiguration, 1);
return;
}
nr_rrc_ue_process_rrcReconfiguration(rrc, 0, RRCReconfiguration);
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, RRCReconfiguration);
}
break;
case nr_RadioBearerConfigX_r15: {
NR_RadioBearerConfig_t *RadioBearerConfig=NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_RadioBearerConfig,
(void **)&RadioBearerConfig,
(uint8_t *)message,
msg_len);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "NR_RadioBearerConfig decode error\n");
// free the memory
SEQUENCE_free( &asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1 );
return;
}
LOG_D(NR_RRC, "Calling nr_rrc_ue_process_RadioBearerConfig()with: e_rab_id = %ld, drbID = %ld, cipher_algo = %ld, key = %ld \n",
RadioBearerConfig->drb_ToAddModList->list.array[0]->cnAssociation->choice.eps_BearerIdentity,
RadioBearerConfig->drb_ToAddModList->list.array[0]->drb_Identity,
RadioBearerConfig->securityConfig->securityAlgorithmConfig->cipheringAlgorithm,
*RadioBearerConfig->securityConfig->keyToUse);
nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)RadioBearerConfig);
ASN_STRUCT_FREE(asn_DEF_NR_RadioBearerConfig, RadioBearerConfig);
}
break;
default:
AssertFatal(1==0,"Unknown message %d\n",nsa_message_type);
break;
}
}
/**
* @brief Verify UE capabilities parameters against CL-fed params
* (e.g. number of physical TX antennas)
*/
static bool verify_ue_cap(NR_UE_NR_Capability_t *UE_NR_Capability, int nb_antennas_tx)
{
NR_FeatureSetUplink_t *ul_feature_setup = UE_NR_Capability->featureSets->featureSetsUplink->list.array[0];
int srs_ant_ports = 1 << ul_feature_setup->supportedSRS_Resources->maxNumberSRS_Ports_PerResource;
AssertFatal(srs_ant_ports <= nb_antennas_tx, "SRS antenna ports (%d) > nb_antennas_tx (%d)\n", srs_ant_ports, nb_antennas_tx);
return true;
}
NR_UE_RRC_INST_t* nr_rrc_init_ue(char* uecap_file, int instance_id, int num_ant_tx)
{
AssertFatal(instance_id < MAX_NUM_NR_UE_INST, "RRC instance %d out of bounds\n", instance_id);
AssertFatal(NR_UE_rrc_inst[instance_id] == NULL, "RRC instance %d already initialized\n", instance_id);
NR_UE_rrc_inst[instance_id] = calloc_or_fail(1, sizeof(NR_UE_RRC_INST_t));
NR_UE_RRC_INST_t *rrc = NR_UE_rrc_inst[instance_id];
rrc->ue_id = instance_id;
// fill UE-NR-Capability @ UE-CapabilityRAT-Container here.
rrc->selected_plmn_identity = 1;
rrc->ra_trigger = RA_NOT_RUNNING;
rrc->dl_bwp_id = 0;
rrc->ul_bwp_id = 0;
rrc->as_security_activated = false;
rrc->sched_reconfsync_sib1 = false;
rrc->detach_after_release = false;
rrc->reconfig_after_reestab = false;
/* 5G-S-TMSI starts unset (sentinel UINT64_MAX). NAS_5GMM_IND populates it on registration */
rrc->fiveG_S_TMSI = UINT64_MAX;
rrc->access_barred = false;
FILE *f = NULL;
if (uecap_file)
f = fopen(uecap_file, "r");
if (f) {
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
rewind(f);
AssertFatal(file_size <= 1024 * 1024,
"UE Capabilities XER file %s is too large (%ld bytes, max 1MB)\n", uecap_file, file_size);
char *UE_NR_Capability_xer = malloc_or_fail(file_size);
size_t size = fread(UE_NR_Capability_xer, 1, file_size, f);
if (size == 0) {
LOG_E(NR_RRC, "UE Capabilities XER file %s: read error\n", uecap_file);
} else {
asn_dec_rval_t dec_rval =
xer_decode(0, &asn_DEF_NR_UE_NR_Capability, (void *)&rrc->UECap.UE_NR_Capability, UE_NR_Capability_xer, size);
assert(dec_rval.code == RC_OK);
}
free(UE_NR_Capability_xer);
fclose(f);
/* Verify consistency of num PHY antennas vs UE Capabilities */
verify_ue_cap(rrc->UECap.UE_NR_Capability, num_ant_tx);
}
memset(&rrc->timers_and_constants, 0, sizeof(rrc->timers_and_constants));
set_default_timers_and_constants(&rrc->timers_and_constants);
for (int j = 0; j < NR_NUM_SRB; j++)
rrc->Srb[j] = RB_NOT_PRESENT;
for (int j = 1; j <= MAX_DRBS_PER_UE; j++)
set_DRB_status(rrc, j, RB_NOT_PRESENT);
// SRB0 activated by default
rrc->Srb[0] = RB_ESTABLISHED;
for (int j = 0; j < NR_MAX_NUM_LCID; j++)
rrc->active_RLC_entity[j] = false;
for (int i = 0; i < NB_CNX_UE; i++) {
rrcPerNB_t *ptr = &rrc->perNB[i];
ptr->SInfo = (NR_UE_RRC_SI_INFO){0};
ptr->l3_measurements = (l3_measurements_t){0};
ptr->l3_measurements.ssb_filter_coeff_rsrp = 1.0f;
ptr->l3_measurements.csi_RS_filter_coeff_rsrp = 1.0f;
init_SI_timers(&ptr->SInfo);
}
init_sidelink(rrc);
return rrc;
}
bool check_si_validity(NR_UE_RRC_SI_INFO *SI_info, int si_type)
{
switch (si_type) {
case NR_SIB_TypeInfo__type_sibType2:
if (!SI_info->sib2_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType3:
if (!SI_info->sib3_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType4:
if (!SI_info->sib4_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType5:
if (!SI_info->sib5_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType6:
if (!SI_info->sib6_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType7:
if (!SI_info->sib7_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType8:
if (!SI_info->sib8_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType9:
if (!SI_info->sib9_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType10_v1610:
if (!SI_info->sib10_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType11_v1610:
if (!SI_info->sib11_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType12_v1610:
if (!SI_info->sib12_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType13_v1610:
if (!SI_info->sib13_validity)
return false;
break;
case NR_SIB_TypeInfo__type_sibType14_v1610:
if (!SI_info->sib14_validity)
return false;
break;
default :
AssertFatal(false, "Invalid SIB type %d\n", si_type);
}
return true;
}
bool check_si_validity_r17(NR_UE_RRC_SI_INFO_r17 *SI_info, int si_type)
{
switch (si_type) {
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType15:
if (!SI_info->sib15_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType16:
if (!SI_info->sib16_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType17:
if (!SI_info->sib17_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType18:
if (!SI_info->sib18_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType19:
if (!SI_info->sib19_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType20:
if (!SI_info->sib20_validity)
return false;
break;
case NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType21:
if (!SI_info->sib21_validity)
return false;
break;
default :
AssertFatal(false, "Invalid SIB r17 type %d\n", si_type);
}
return true;
}
static int check_si_status(NR_UE_RRC_SI_INFO *SI_info)
{
// schedule reception of SIB1 if RRC doesn't have it
if (!SI_info->sib1_validity)
return 1;
else {
for (int j = 0; j < MAX_SI_GROUPS; j++) {
if (!SI_info->default_otherSI_map[j])
continue;
// Check if RRC has configured default SI
// TODO can be used for on demand SI when (if) implemented
for (int i = 2; i < 22; i++) {
if (!((SI_info->default_otherSI_map[j] >> (i - 2)) & 0x01))
continue;
// if RRC has no valid version of one of the default configured SI
// Then schedule reception of otherSI
if (i < 15) {
if (!check_si_validity(SI_info, i - 2))
return 2 + j;
} else {
if (!check_si_validity_r17(&SI_info->SInfo_r17, i - 15))
return 2 + j;
}
}
}
}
return 0;
}
/*brief decode BCCH-BCH (MIB) message*/
static void nr_rrc_ue_decode_NR_BCCH_BCH_Message(NR_UE_RRC_INST_t *rrc,
const uint8_t gNB_index,
const uint32_t phycellid,
const long ssb_arfcn,
uint8_t *const bufferP,
const uint8_t buffer_len)
{
// MIB received on the target cell. Now process target ntncfg once new timing is received.
if (phycellid == rrc->phyCellID && rrc->target_ntncfg) {
rrc->process_target_ntncfg = true;
}
NR_BCCH_BCH_Message_t *bcch_message = NULL;
if (rrc->phyCellID != phycellid || rrc->arfcn_ssb != ssb_arfcn) {
LOG_I(NR_RRC,
"[UE %ld] BCCH update: phyCellID %d->%u, arfcn_ssb %ld->%ld\n",
rrc->ue_id,
rrc->phyCellID,
phycellid,
rrc->arfcn_ssb,
ssb_arfcn);
}
rrc->phyCellID = phycellid;
rrc->arfcn_ssb = ssb_arfcn;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_BCCH_BCH_Message,
(void **)&bcch_message,
(const void *)bufferP,
buffer_len);
if ((dec_rval.code != RC_OK) || (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "NR_BCCH_BCH decode error\n");
return;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_BCCH_BCH_Message, (void *)bcch_message);
// Actions following cell selection while T311 is running
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
if (nr_timer_is_active(&timers->T311)) {
nr_timer_stop(&timers->T311);
rrc->ra_trigger = RRC_CONNECTION_REESTABLISHMENT;
// apply the default MAC Cell Group configuration
// (done at MAC by calling nr_ue_mac_default_configs)
// apply the timeAlignmentTimerCommon included in SIB1
// not used
}
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
bool barred = rrc->access_barred || bcch_message->message.choice.mib->cellBarred == NR_MIB__cellBarred_barred;
int get_sib = 0;
if (IS_SA_MODE(get_softmodem_params())
&& !SI_info->sib_pending
&& bcch_message->message.present == NR_BCCH_BCH_MessageType_PR_mib
&& !barred
&& rrc->nrRrcState != RRC_STATE_DETACH_NR) {
// to schedule MAC to get SI if required
get_sib = check_si_status(SI_info);
}
if (bcch_message->message.present == NR_BCCH_BCH_MessageType_PR_mib) {
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_MIB;
nr_mac_rrc_config_mib_t *config_mib = &rrc_msg.payload.config_mib;
config_mib->bcch = bcch_message;
config_mib->access_barred = barred;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
if (get_sib) {
SI_info->sib_pending = true;
nr_mac_rrc_message_t sib_msg = {0};
sib_msg.payload_type = NR_MAC_RRC_SCHED_SIB;
sib_msg.payload.sched_sib.get_sib = get_sib;
nr_rrc_send_msg_to_mac(rrc, &sib_msg);
}
} else {
LOG_E(NR_RRC, "RRC-received BCCH message is not a MIB\n");
ASN_STRUCT_FREE(asn_DEF_NR_BCCH_BCH_Message, bcch_message);
}
return;
}
static void nr_rrc_ue_prepare_RRCReestablishmentRequest(NR_UE_RRC_INST_t *rrc)
{
uint8_t buffer[1024];
int buf_size = do_RRCReestablishmentRequest(buffer, rrc->reestablishment_cause, rrc->phyCellID, rrc->rnti); // old rnti
nr_rlc_srb_recv_sdu(rrc->ue_id, 0, buffer, buf_size);
}
static void nr_rrc_prepare_msg3_payload(NR_UE_RRC_INST_t *rrc)
{
if (!IS_SA_MODE(get_softmodem_params()))
return;
switch (rrc->ra_trigger) {
case RRC_CONNECTION_SETUP:
// preparing RRC setup request payload in advance
nr_rrc_ue_prepare_RRCSetupRequest(rrc);
break;
case RRC_CONNECTION_REESTABLISHMENT:
// preparing MSG3 for re-establishment in advance
nr_rrc_ue_prepare_RRCReestablishmentRequest(rrc);
break;
default:
AssertFatal(false, "RA trigger not implemented\n");
}
}
static void nr_rrc_handle_msg3_indication(NR_UE_RRC_INST_t *rrc, rnti_t rnti)
{
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
switch (rrc->ra_trigger) {
case RRC_CONNECTION_SETUP:
// After SIB1 is received, prepare RRCConnectionRequest
rrc->rnti = rnti;
// start timer T300
nr_timer_start(&tac->T300);
break;
case RRC_CONNECTION_REESTABLISHMENT:
rrc->rnti = rnti;
nr_timer_start(&tac->T301);
int srb_id = 1;
// re-establish PDCP for SRB1
// (and suspend integrity protection and ciphering for SRB1)
nr_pdcp_entity_security_keys_and_algos_t null_security_parameters = {0};
nr_pdcp_reestablishment(rrc->ue_id, srb_id, true, &null_security_parameters);
// re-establish RLC for SRB1
int lc_id = nr_rlc_get_lcid_from_rb(rrc->ue_id, true, 1);
nr_rlc_reestablish_entity(rrc->ue_id, lc_id);
// apply the specified configuration defined in 9.2.1 for SRB1
nr_rlc_reconfigure_entity(rrc->ue_id, lc_id, NULL);
// resume SRB1
rrc->Srb[srb_id] = RB_ESTABLISHED;
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_RESUME_RB;
rrc_msg.payload.resume_rb.is_srb = true;
rrc_msg.payload.resume_rb.rb_id = 1;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
break;
case DURING_HANDOVER:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
case NON_SYNCHRONISED:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
case TRANSITION_FROM_RRC_INACTIVE:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
case TO_ESTABLISH_TA:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
case REQUEST_FOR_OTHER_SI:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
case BEAM_FAILURE_RECOVERY:
AssertFatal(1==0, "ra_trigger not implemented yet!\n");
break;
default:
AssertFatal(1==0, "Invalid ra_trigger value!\n");
break;
}
}
static void nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(NR_UE_RRC_INST_t *rrc,
const uint8_t gNB_index,
uint8_t *const Sdu,
const uint8_t Sdu_len,
int hfn,
int frame,
int slot)
{
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
SI_info->sib_pending = false;
if (Sdu_len == 0) // decoding failed in L2
return;
NR_BCCH_DL_SCH_Message_t *bcch_message = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_BCCH_DL_SCH_Message,
(void **)&bcch_message,
(const void *)Sdu,
Sdu_len);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "[UE %ld] Failed to decode BCCH_DLSCH_MESSAGE (%zu bits)\n", rrc->ue_id, dec_rval.consumed);
log_dump(NR_RRC, Sdu, Sdu_len, LOG_DUMP_CHAR," Received bytes:\n");
// free the memory
SEQUENCE_free(&asn_DEF_NR_BCCH_DL_SCH_Message, (void *)bcch_message, 1);
return;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_BCCH_DL_SCH_Message,(void *)bcch_message);
}
if (bcch_message->message.present == NR_BCCH_DL_SCH_MessageType_PR_c1) {
switch (bcch_message->message.choice.c1->present) {
case NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformationBlockType1:
nr_rrc_process_sib1(rrc, SI_info, bcch_message->message.choice.c1->choice.systemInformationBlockType1);
// mac layer will free after usage the sib1
bcch_message->message.choice.c1->choice.systemInformationBlockType1 = NULL;
break;
case NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformation:
LOG_I(NR_RRC, "[UE %ld] %d:%d Decoding SI\n", rrc->ue_id, frame, slot);
NR_SystemInformation_t *si = bcch_message->message.choice.c1->choice.systemInformation;
nr_decode_SI(SI_info, si, rrc, hfn, frame);
break;
case NR_BCCH_DL_SCH_MessageType__c1_PR_NOTHING:
default:
break;
}
}
SEQUENCE_free(&asn_DEF_NR_BCCH_DL_SCH_Message, bcch_message, ASFM_FREE_EVERYTHING);
}
static void rrc_ue_generate_RRCSetupComplete(NR_UE_RRC_INST_t *rrc, const uint8_t Transaction_id)
{
uint8_t buffer[100];
as_nas_info_t initialNasMsg = {0};
if (IS_SA_MODE(get_softmodem_params())) {
/* 3GPP TS 38.331:
* - 5.3.3.1: RRC connection establishment is also used to transfer the initial NAS dedicated information
* message from the UE to the network.
* - 5.3.3.4: set the dedicatedNAS-Message to include the information received from upper layers.
* In paging-triggered service resumption (TS 24.501 service request procedure), the Service Request NAS PDU
* is therefore carried here as the initial NAS message in RRCSetupComplete. */
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
if (rrc->pending_initial_nas.nas_data && rrc->pending_initial_nas.length > 0) {
/* Deferred NAS PDU is placed in dedicatedNAS-Message (TS 38.331 §5.3.3.4). It is the initial NAS message that
* starts CM-IDLE to CM-CONNECTED transition (TS 33.501 §6.8.1.2.1, TS 24.501). */
initialNasMsg = rrc->pending_initial_nas;
/* Clear RRC copy of pointer after shallow copy: NAS payload is owned by initialNasMsg */
rrc->pending_initial_nas.nas_data = NULL;
rrc->pending_initial_nas.length = 0;
LOG_I(NR_RRC, "[UE %ld] Using pending initial NAS message for RRCSetupComplete\n", rrc->ue_id);
/* TS 33.501 §6.8.1.2.2: UE derives KgNB from KAMF using NAS UL COUNT of the NAS message that initiated
* CM-IDLE to CM-CONNECTED transition. §6.2.3.2 / Annex A.9 tie initial KgNB to ngKSI + that COUNT. In this case,
* NAS holds the derived KgNB value: copy into RRC so AS SMC and RRC keys (§6.2.3.1, §6.5) match after RRC release
* cleared AS keys (§6.8.1.2.1). */
if (nas->security_container && nas->security_container->integrity_context)
memcpy(rrc->kgnb, nas->security.kgnb, sizeof(rrc->kgnb));
} else {
// Send Initial NAS message (Registration Request) before Security Mode control procedure
generateRegistrationRequest(&initialNasMsg, nas, false);
}
if (!initialNasMsg.nas_data) {
LOG_E(NR_RRC, "Failed to complete RRCSetup. NAS InitialUEMessage message not found.\n");
return;
}
} else {
initialNasMsg.length = sizeof(nr_nas_attach_req_imsi_dummy_NSA_case);
initialNasMsg.nas_data = malloc_or_fail(initialNasMsg.length);
memcpy(initialNasMsg.nas_data, nr_nas_attach_req_imsi_dummy_NSA_case, initialNasMsg.length);
}
// Encode RRCSetupComplete
int size = do_RRCSetupComplete(buffer,
sizeof(buffer),
Transaction_id,
rrc->selected_plmn_identity,
rrc->ra_trigger == RRC_CONNECTION_SETUP,
rrc->fiveG_S_TMSI,
(const uint32_t)initialNasMsg.length,
(const char*)initialNasMsg.nas_data);
// Free dynamically allocated data (heap allocated in both SA and NSA)
free(initialNasMsg.nas_data);
LOG_I(NR_RRC, "[UE %ld][RAPROC] Logical Channel UL-DCCH (SRB1), Generating RRCSetupComplete (bytes%d)\n", rrc->ue_id, size);
int srb_id = 1; // RRC setup complete on SRB1
LOG_D(NR_RRC, "[RRC_UE %ld] PDCP_DATA_REQ/%d Bytes RRCSetupComplete ---> %d\n", rrc->ue_id, size, srb_id);
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL);
}
static void nr_rrc_rrcsetup_fallback(NR_UE_RRC_INST_t *rrc)
{
LOG_W(NR_RRC,
"[UE %ld] Received RRCSetup in response to %s request\n",
rrc->ue_id, rrc->ra_trigger == RRC_CONNECTION_REESTABLISHMENT ? "RRCReestablishment" : "RRCResume");
// discard any stored UE Inactive AS context and suspendConfig
// TODO
// discard any current AS security context including
// K_RRCenc key, the K_RRCint key, the K_UPint key and the K_UPenc key
// TODO only kgnb is stored
memset(rrc->kgnb, 0, sizeof(rrc->kgnb));
rrc->as_security_activated = false;
// release the RRC configuration except for the default L1 parameter values,
// default MAC Cell Group configuration and CCCH configuration
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_RESET;
rrc_msg.payload.config_reset.cause = RRC_SETUP_REESTAB_RESUME;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
// release radio resources for all established RBs except SRB0,
// including release of the associated PDCP entities and of SDAP
for (int i = 1; i <= MAX_DRBS_PER_UE; i++) {
if (get_DRB_status(rrc, i) != RB_NOT_PRESENT) {
set_DRB_status(rrc, i, RB_NOT_PRESENT);
nr_pdcp_release_drb(rrc->ue_id, i);
}
}
for (int i = 1; i < NR_NUM_SRB; i++) {
if (rrc->Srb[i] != RB_NOT_PRESENT) {
rrc->Srb[i] = RB_NOT_PRESENT;
nr_pdcp_release_srb(rrc->ue_id, i);
}
}
for (int i = 1; i < NR_MAX_NUM_LCID; i++) {
nr_rrc_release_rlc_entity(rrc, i);
}
nr_sdap_delete_ue_entities(rrc->ue_id);
// indicate to upper layers fallback of the RRC connection
// TODO
// stop timer T380, if running
// TODO not implemented yet
}
static void nr_rrc_process_rrcsetup(NR_UE_RRC_INST_t *rrc, const NR_RRCSetup_t *rrcSetup)
{
// if the RRCSetup is received in response to an RRCReestablishmentRequest
// or RRCResumeRequest or RRCResumeRequest1
if (rrc->ra_trigger == RRC_CONNECTION_REESTABLISHMENT || rrc->ra_trigger == RRC_RESUME_REQUEST)
nr_rrc_rrcsetup_fallback(rrc);
// perform the cell group configuration procedure in accordance with the received masterCellGroup
bool ret = nr_rrc_ue_process_masterCellGroup(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup, NULL, 0);
if (!ret)
return;
// perform the radio bearer configuration procedure in accordance with the received radioBearerConfig
nr_rrc_ue_process_RadioBearerConfig(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig);
// TODO (not handled) if stored, discard the cell reselection priority information provided by
// the cellReselectionPriorities or inherited from another RAT
// stop timer T300, T301, T319, T320 if running;
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
nr_timer_stop(&timers->T300);
nr_timer_stop(&timers->T301);
nr_timer_stop(&timers->T319);
nr_timer_stop(&timers->T320);
// if T390 (not implemented) and T302 are running
// stop timer
// perform the actions as specified in 5.3.14.4
nr_timer_stop(&timers->T302);
handle_302_expired_stopped(rrc);
// if the RRCSetup is received in response to an RRCResumeRequest, RRCResumeRequest1 or RRCSetupRequest
// enter RRC_CONNECTED
rrc->nrRrcState = RRC_STATE_CONNECTED_NR;
// Indicate to NAS that the RRC connection has been established (5.3.1.3 of 3GPP TS 24.501)
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NR_NAS_CONN_ESTABLISH_IND);
itti_send_msg_to_task(TASK_NAS_NRUE, rrc->ue_id, msg_p);
// resetting the RA trigger state after receiving MSG4 with RRCSetup
rrc->ra_trigger = RA_NOT_RUNNING;
// set the content of RRCSetupComplete message
// TODO procedues described in 5.3.3.4 seems more complex than what we actualy do
rrc_ue_generate_RRCSetupComplete(rrc, rrcSetup->rrc_TransactionIdentifier);
}
static void nr_rrc_process_rrcreject(NR_UE_RRC_INST_t *rrc, const NR_RRCReject_t *rrcReject)
{
// stop timer T300, T302, T319 if running;
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
nr_timer_stop(&timers->T300);
nr_timer_stop(&timers->T302);
nr_timer_stop(&timers->T319);
// reset MAC and release the default MAC Cell Group configuration
NR_UE_MAC_reset_cause_t cause = REJECT;
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_RESET;
rrc_msg.payload.config_reset.cause = cause;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
// if waitTime is configured in the RRCReject: start timer T302, with the timer value set to the waitTime
NR_RejectWaitTime_t *waitTime = NULL;
if (rrcReject->criticalExtensions.present == NR_RRCReject__criticalExtensions_PR_rrcReject) {
NR_RRCReject_IEs_t *ies = rrcReject->criticalExtensions.choice.rrcReject;
waitTime = ies->waitTime; // Wait time value in seconds
}
if (waitTime) {
nr_timer_setup(&timers->T302, *waitTime * 1000, 10);
nr_timer_start(&timers->T302);
rrc->access_barred = true;
} else {
LOG_W(RRC, "Error: waitTime should be always included in RRCReject message\n");
}
// TODO if RRCReject is received in response to a request from upper layers
// inform the upper layer that access barring is applicable for all access categories except categories '0' and '2'
// TODO if RRCReject is received in response to an RRCSetupRequest
// inform upper layers about the failure to setup the RRC connection, upon which the procedure ends
// TODO else if RRCReject is received in response to an RRCResumeRequest or an RRCResumeRequest1
// Resume not implemented yet
}
static int8_t nr_rrc_ue_decode_ccch(NR_UE_RRC_INST_t *rrc, const NRRrcMacCcchDataInd *ind)
{
NR_DL_CCCH_Message_t *dl_ccch_msg = NULL;
asn_dec_rval_t dec_rval;
int rval=0;
LOG_D(RRC, "[NR UE%ld] Decoding DL-CCCH message (%d bytes), State %d\n", rrc->ue_id, ind->sdu_size, rrc->nrRrcState);
dec_rval = uper_decode(NULL, &asn_DEF_NR_DL_CCCH_Message, (void **)&dl_ccch_msg, ind->sdu, ind->sdu_size, 0, 0);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_DL_CCCH_Message, (void *)dl_ccch_msg);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(RRC, "[UE %ld] Failed to decode DL-CCCH-Message (%zu bytes)\n", rrc->ue_id, dec_rval.consumed);
return -1;
}
if (dl_ccch_msg->message.present == NR_DL_CCCH_MessageType_PR_c1) {
switch (dl_ccch_msg->message.choice.c1->present) {
case NR_DL_CCCH_MessageType__c1_PR_NOTHING:
LOG_I(NR_RRC, "[UE%ld] Received PR_NOTHING on DL-CCCH-Message\n", rrc->ue_id);
rval = 0;
break;
case NR_DL_CCCH_MessageType__c1_PR_rrcReject:
LOG_W(NR_RRC, "[UE%ld] Logical Channel DL-CCCH (SRB0), Received RRCReject \n", rrc->ue_id);
nr_rrc_process_rrcreject(rrc, dl_ccch_msg->message.choice.c1->choice.rrcReject);
rval = 0;
break;
case NR_DL_CCCH_MessageType__c1_PR_rrcSetup:
LOG_I(NR_RRC, "[UE%ld][RAPROC] Logical Channel DL-CCCH (SRB0), Received NR_RRCSetup\n", rrc->ue_id);
nr_rrc_process_rrcsetup(rrc, dl_ccch_msg->message.choice.c1->choice.rrcSetup);
rval = 0;
break;
default:
LOG_E(NR_RRC, "[UE%ld] Unknown message\n", rrc->ue_id);
rval = -1;
break;
}
}
ASN_STRUCT_FREE(asn_DEF_NR_DL_CCCH_Message, dl_ccch_msg);
return rval;
}
/** @brief Decode a PCCH paging message and scan its paging records (TS 38.331 §5.3.2.3).
* @return -1 on decode error, 0 if no match is found, 1 if a record matches the UE */
static int8_t nr_rrc_ue_decode_pcch(NR_UE_RRC_INST_t *rrc, const byte_array_t pcch)
{
// Paging is only relevant in RRC_IDLE and RRC_INACTIVE.
if (rrc->nrRrcState != RRC_STATE_IDLE_NR && rrc->nrRrcState != RRC_STATE_INACTIVE_NR) {
LOG_D(NR_RRC, "[UE %ld] Ignoring PCCH in RRC state %d\n", rrc->ue_id, rrc->nrRrcState);
return 0;
}
LOG_D(NR_RRC, "[UE %ld] Decoding PCCH message (%zu bytes), State %d\n", rrc->ue_id, pcch.len, rrc->nrRrcState);
nr_paging_params_t params[NR_PCCH_MAX_PAGING_RECORDS];
int count = 0;
if (nr_pcch_decode(pcch, params, &count) != 0) {
LOG_E(NR_RRC, "[UE %ld] Failed to decode PCCH message (%zu bytes)\n", rrc->ue_id, pcch.len);
log_dump(NR_RRC, pcch.buf, pcch.len, LOG_DUMP_CHAR, " Received bytes:\n");
return -1;
}
LOG_D(NR_RRC, "[UE %ld] Received Paging message with %d record(s)\n", rrc->ue_id, count);
const uint64_t ue_fiveg_s_tmsi = rrc->fiveG_S_TMSI & ((1ULL << 48) - 1);
for (int i = 0; i < count; i++) {
if (params[i].ue_identity_type == NR_PagingUE_Identity_PR_ng_5G_S_TMSI
&& params[i].ue_identity.fiveg_s_tmsi == ue_fiveg_s_tmsi) {
LOG_I(NR_RRC, "[UE %ld] Paging record %d matches 5G-S-TMSI=0x%012lu\n", rrc->ue_id, i, ue_fiveg_s_tmsi);
return 1;
}
}
LOG_D(NR_RRC, "[UE %ld] No paging match found in PagingRecordList\n", rrc->ue_id);
return 0;
}
static void nr_rrc_ue_process_securityModeCommand(NR_UE_RRC_INST_t *ue_rrc,
NR_SecurityModeCommand_t *const securityModeCommand,
int srb_id,
const uint8_t *msg,
int msg_size,
const nr_pdcp_integrity_data_t *msg_integrity)
{
LOG_I(NR_RRC, "Receiving from SRB1 (DL-DCCH), Processing securityModeCommand\n");
AssertFatal(securityModeCommand->criticalExtensions.present == NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand,
"securityModeCommand->criticalExtensions.present (%d) != "
"NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand\n",
securityModeCommand->criticalExtensions.present);
NR_SecurityConfigSMC_t *securityConfigSMC =
&securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC;
switch (securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm) {
case NR_CipheringAlgorithm_nea0:
case NR_CipheringAlgorithm_nea1:
case NR_CipheringAlgorithm_nea2:
LOG_I(NR_RRC, "Security algorithm is set to nea%ld\n",
securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm);
break;
default:
AssertFatal(0, "Security algorithm not known/supported\n");
}
ue_rrc->cipheringAlgorithm = securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm;
ue_rrc->integrityProtAlgorithm = 0;
if (securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm != NULL) {
switch (*securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm) {
case NR_IntegrityProtAlgorithm_nia0:
case NR_IntegrityProtAlgorithm_nia1:
case NR_IntegrityProtAlgorithm_nia2:
LOG_I(NR_RRC, "Integrity protection algorithm is set to nia%ld\n", *securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm);
break;
default:
AssertFatal(0, "Integrity algorithm not known/supported\n");
}
ue_rrc->integrityProtAlgorithm = *securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm;
}
nr_pdcp_entity_security_keys_and_algos_t security_parameters;
nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_parameters.ciphering_key);
nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_parameters.integrity_key);
log_dump(NR_RRC, ue_rrc->kgnb, 32, LOG_DUMP_CHAR, "deriving kRRCenc, kRRCint from KgNB=");
/* for SecurityModeComplete, ciphering is not activated yet, only integrity */
security_parameters.ciphering_algorithm = 0;
security_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
// configure lower layers to apply SRB integrity protection and ciphering
for (int i = 1; i < NR_NUM_SRB; i++) {
if (ue_rrc->Srb[i] == RB_ESTABLISHED)
nr_pdcp_config_set_security(ue_rrc->ue_id, i, true, &security_parameters);
}
NR_UL_DCCH_Message_t ul_dcch_msg = {0};
ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1;
asn1cCalloc(ul_dcch_msg.message.choice.c1, c1);
// the SecurityModeCommand message needs to pass the integrity protection check
// for the UE to declare AS security to be activated
bool integrity_pass = nr_pdcp_check_integrity_srb(ue_rrc->ue_id, srb_id, msg, msg_size, msg_integrity);
if (!integrity_pass) {
/* - continue using the configuration used prior to the reception of the SecurityModeCommand message, i.e.
* neither apply integrity protection nor ciphering.
* - submit the SecurityModeFailure message to lower layers for transmission, upon which the procedure ends.
*/
LOG_E(NR_RRC, "integrity of SecurityModeCommand failed, reply with SecurityModeFailure\n");
c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeFailure;
asn1cCalloc(c1->choice.securityModeFailure, modeFailure);
modeFailure->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier;
modeFailure->criticalExtensions.present = NR_SecurityModeFailure__criticalExtensions_PR_securityModeFailure;
asn1cCalloc(modeFailure->criticalExtensions.choice.securityModeFailure, ext);
ext->nonCriticalExtension = NULL;
uint8_t buffer[200];
asn_enc_rval_t enc_rval =
uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, sizeof(buffer));
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg);
/* disable both ciphering and integrity */
nr_pdcp_entity_security_keys_and_algos_t null_security_parameters = {0};
for (int i = 1; i < NR_NUM_SRB; i++) {
if (ue_rrc->Srb[i] == RB_ESTABLISHED)
nr_pdcp_config_set_security(ue_rrc->ue_id, i, true, &null_security_parameters);
}
srb_id = 1; // SecurityModeFailure in SRB1
nr_pdcp_data_req_srb(ue_rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL);
return;
}
/* integrity passed, send SecurityModeComplete */
c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete;
asn1cCalloc(c1->choice.securityModeComplete, modeComplete);
modeComplete->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier;
modeComplete->criticalExtensions.present = NR_SecurityModeComplete__criticalExtensions_PR_securityModeComplete;
asn1cCalloc(modeComplete->criticalExtensions.choice.securityModeComplete, ext);
ext->nonCriticalExtension = NULL;
LOG_I(NR_RRC,
"Receiving from SRB1 (DL-DCCH), encoding securityModeComplete, rrc_TransactionIdentifier: %ld\n",
securityModeCommand->rrc_TransactionIdentifier);
uint8_t buffer[200];
asn_enc_rval_t enc_rval =
uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, sizeof(buffer));
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg);
}
log_dump(NR_RRC, buffer, 16, LOG_DUMP_CHAR, "securityModeComplete payload: ");
LOG_D(NR_RRC, "securityModeComplete Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg);
for (int i = 0; i < (enc_rval.encoded + 7) / 8; i++) {
LOG_T(NR_RRC, "%02x.", buffer[i]);
}
LOG_T(NR_RRC, "\n");
ue_rrc->as_security_activated = true;
srb_id = 1; // SecurityModeComplete in SRB1
nr_pdcp_data_req_srb(ue_rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL);
/* after encoding SecurityModeComplete we activate both ciphering and integrity */
security_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
// configure lower layers to apply SRB integrity protection and ciphering
for (int i = 1; i < NR_NUM_SRB; i++) {
if (ue_rrc->Srb[i] == RB_ESTABLISHED)
nr_pdcp_config_set_security(ue_rrc->ue_id, i, true, &security_parameters);
}
}
static void nr_rrc_ue_generate_RRCReconfigurationComplete(NR_UE_RRC_INST_t *rrc, const int srb_id, const uint8_t Transaction_id)
{
uint8_t buffer[32];
int size = do_NR_RRCReconfigurationComplete(buffer, sizeof(buffer), Transaction_id);
LOG_I(NR_RRC, " Logical Channel UL-DCCH (SRB1), Generating RRCReconfigurationComplete (bytes %d)\n", size);
AssertFatal(srb_id == 1 || srb_id == 3, "Invalid SRB ID %d\n", srb_id);
LOG_D(RLC,
"PDCP_DATA_REQ/%d Bytes (RRCReconfigurationComplete) "
"--->][PDCP][RB %02d]\n",
size,
srb_id);
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL);
}
static void nr_rrc_ue_generate_rrcReestablishmentComplete(const NR_UE_RRC_INST_t *rrc,
const NR_RRCReestablishment_t *rrcReestablishment)
{
uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
int size = do_RRCReestablishmentComplete(buffer, NR_RRC_BUF_SIZE, rrcReestablishment->rrc_TransactionIdentifier);
LOG_I(NR_RRC, "[RAPROC] Logical Channel UL-DCCH (SRB1), Generating RRCReestablishmentComplete (bytes %d)\n", size);
int srb_id = 1; // RRC re-establishment complete on SRB1
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL);
}
/** @brief Process RRCReestablishment message
* This function processes the RRCReestablishment message received from the gNB,
* implementing procedures as described in 38.331 section 5.3.7.5 */
static void nr_rrc_ue_process_rrcReestablishment(NR_UE_RRC_INST_t *rrc,
const int gNB_index,
const NR_RRCReestablishment_t *rrcReestablishment,
int srb_id,
const uint8_t *msg,
int msg_size,
const nr_pdcp_integrity_data_t *msg_integrity)
{
// stop timer T301
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
nr_timer_stop(&timers->T301);
NR_RRCReestablishment_IEs_t *ies = rrcReestablishment->criticalExtensions.choice.rrcReestablishment;
AssertFatal(ies, "Not expecting RRCReestablishment_IEs to be NULL\n");
// Update KgNB based on the current K gNB key or the NH,
// using the received nextHopChainingCount (per 33.501 6.9.2.3.4)
// received from RRCReestablishment and update the stored value in rrc->nhcc
int8_t received_ncc = ies->nextHopChainingCount;
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
const uint8_t *kamf = nas->security.kamf;
nr_update_kgnb_from_ncc(rrc, kamf, received_ncc);
// derive the K_RRCenc key associated with the previously configured cipheringAlgorithm
// derive the K_RRCint key associated with the previously configured integrityProtAlgorithm
nr_pdcp_entity_security_keys_and_algos_t security_parameters;
security_parameters.ciphering_algorithm = rrc->cipheringAlgorithm;
security_parameters.integrity_algorithm = rrc->integrityProtAlgorithm;
nr_derive_key(RRC_ENC_ALG, rrc->cipheringAlgorithm, rrc->kgnb, security_parameters.ciphering_key);
nr_derive_key(RRC_INT_ALG, rrc->integrityProtAlgorithm, rrc->kgnb, security_parameters.integrity_key);
// configure lower layers to resume integrity protection for SRB1
// configure lower layers to resume ciphering for SRB1
AssertFatal(srb_id == 1, "rrcReestablishment SRB-ID %d, should be 1\n", srb_id);
nr_pdcp_config_set_security(rrc->ue_id, srb_id, true, &security_parameters);
// request lower layers to verify the integrity protection of the RRCReestablishment message
// using the previously configured algorithm and the K_RRCint key
bool integrity_pass = nr_pdcp_check_integrity_srb(rrc->ue_id, srb_id, msg, msg_size, msg_integrity);
// if the integrity protection check of the RRCReestablishment message fails
// perform the actions upon going to RRC_IDLE as specified in 5.3.11
// with release cause 'RRC connection failure', upon which the procedure ends
if (!integrity_pass) {
LOG_W(NR_RRC, "Integrity of RRCReestablishment failed, going to IDLE\n");
NR_Release_Cause_t release_cause = RRC_CONNECTION_FAILURE;
nr_rrc_going_to_IDLE(rrc, release_cause, NULL);
return;
}
// release the measurement gap configuration indicated by the measGapConfig, if configured
rrcPerNB_t *rrcNB = rrc->perNB + gNB_index;
asn1cFreeStruc(asn_DEF_NR_MeasGapConfig, rrcNB->measGapConfig);
// resetting the RA trigger state after receiving MSG4 with RRCReestablishment
rrc->ra_trigger = RA_NOT_RUNNING;
// to flag 1st reconfiguration after reestablishment
rrc->reconfig_after_reestab = true;
// submit the RRCReestablishmentComplete message to lower layers for transmission
nr_rrc_ue_generate_rrcReestablishmentComplete(rrc, rrcReestablishment);
}
static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECapabilityEnquiry_t *UECapabilityEnquiry)
{
NR_UL_DCCH_Message_t ul_dcch_msg = {0};
LOG_I(NR_RRC, "Receiving from SRB1 (DL-DCCH), Processing UECapabilityEnquiry\n");
ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1;
asn1cCalloc(ul_dcch_msg.message.choice.c1, c1);
c1->present = NR_UL_DCCH_MessageType__c1_PR_ueCapabilityInformation;
asn1cCalloc(c1->choice.ueCapabilityInformation, info);
info->rrc_TransactionIdentifier = UECapabilityEnquiry->rrc_TransactionIdentifier;
if (!rrc->UECap.UE_NR_Capability) {
rrc->UECap.UE_NR_Capability = CALLOC(1, sizeof(NR_UE_NR_Capability_t));
asn1cSequenceAdd(rrc->UECap.UE_NR_Capability->rf_Parameters.supportedBandListNR.list, NR_BandNR_t, nr_bandnr);
nr_bandnr->bandNR = 1;
}
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, (void *)rrc->UECap.UE_NR_Capability);
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UE_NR_Capability,
NULL,
(void *)rrc->UECap.UE_NR_Capability,
&rrc->UECap.sdu[0],
MAX_UE_NR_CAPABILITY_SIZE);
AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
rrc->UECap.sdu_size = (enc_rval.encoded + 7) / 8;
LOG_I(PHY, "[RRC]UE NR Capability encoded, %d bytes (%zd bits)\n", rrc->UECap.sdu_size, enc_rval.encoded + 7);
NR_UECapabilityEnquiry_IEs_t *ueCapabilityEnquiry_ie = UECapabilityEnquiry->criticalExtensions.choice.ueCapabilityEnquiry;
AssertFatal(UECapabilityEnquiry->criticalExtensions.present == NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry,
"UECapabilityEnquiry->criticalExtensions.present (%d) != UECapabilityEnquiry__criticalExtensions_PR_c1 (%d)\n",
UECapabilityEnquiry->criticalExtensions.present,NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry);
NR_UECapabilityInformation_t *ueCapabilityInformation = ul_dcch_msg.message.choice.c1->choice.ueCapabilityInformation;
ueCapabilityInformation->criticalExtensions.present = NR_UECapabilityInformation__criticalExtensions_PR_ueCapabilityInformation;
asn1cCalloc(ueCapabilityInformation->criticalExtensions.choice.ueCapabilityInformation, infoIE);
asn1cCalloc(infoIE->ue_CapabilityRAT_ContainerList, UEcapList);
UEcapList->list.count = 0;
for (int i = 0; i < ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.count; i++) {
if (ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.array[i]->rat_Type == NR_RAT_Type_nr) {
/* RAT Container */
NR_UE_CapabilityRAT_Container_t *ue_CapabilityRAT_Container = CALLOC(1, sizeof(NR_UE_CapabilityRAT_Container_t));
ue_CapabilityRAT_Container->rat_Type = NR_RAT_Type_nr;
OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container->ue_CapabilityRAT_Container, (const char *)rrc->UECap.sdu, rrc->UECap.sdu_size);
asn1cSeqAdd(&UEcapList->list, ue_CapabilityRAT_Container);
uint8_t buffer[MAX_UE_NR_CAPABILITY_SIZE + 16];
asn_enc_rval_t enc_rval =
uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, sizeof(buffer));
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg);
}
LOG_I(NR_RRC, "UECapabilityInformation Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8);
int srb_id = 1; // UECapabilityInformation on SRB1
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL);
}
}
/* Free struct members after it's done including locally allocated ue_CapabilityRAT_Container */
ASN_STRUCT_RESET(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg);
}
static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc,
const srb_id_t Srb_id,
const uint8_t *const Buffer,
size_t Buffer_size,
const uint8_t gNB_indexP,
const nr_pdcp_integrity_data_t *msg_integrity)
{
NR_DL_DCCH_Message_t *dl_dcch_msg = NULL;
if (Srb_id != 1 && Srb_id != 2) {
LOG_E(NR_RRC, "Received message on DL-DCCH (SRB%ld), should not have ...\n", Srb_id);
}
LOG_D(NR_RRC, "Decoding DL-DCCH Message\n");
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_DL_DCCH_Message, (void **)&dl_dcch_msg, Buffer, Buffer_size, 0, 0);
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "Failed to decode DL-DCCH (%zu bytes)\n", dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_DL_DCCH_Message, dl_dcch_msg);
return -1;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)dl_dcch_msg);
}
switch (dl_dcch_msg->message.present) {
case NR_DL_DCCH_MessageType_PR_c1: {
struct NR_DL_DCCH_MessageType__c1 *c1 = dl_dcch_msg->message.choice.c1;
switch (c1->present) {
case NR_DL_DCCH_MessageType__c1_PR_NOTHING:
LOG_I(NR_RRC, "Received PR_NOTHING on DL-DCCH-Message\n");
break;
case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: {
nr_rrc_ue_process_rrcReconfiguration(rrc, gNB_indexP, c1->choice.rrcReconfiguration);
if (rrc->reconfig_after_reestab) {
// if this is the first RRCReconfiguration message after successful completion of the RRC re-establishment procedure
// resume SRB2 and DRBs that are suspended
if (rrc->Srb[2] == RB_SUSPENDED) {
rrc->Srb[2] = RB_ESTABLISHED;
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_RESUME_RB;
rrc_msg.payload.resume_rb.is_srb = true;
rrc_msg.payload.resume_rb.rb_id = 2;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
for (int i = 1; i <= MAX_DRBS_PER_UE; i++) {
if (get_DRB_status(rrc, i) == RB_SUSPENDED) {
set_DRB_status(rrc, i, RB_ESTABLISHED);
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_RESUME_RB;
rrc_msg.payload.resume_rb.is_srb = false;
rrc_msg.payload.resume_rb.rb_id = i;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
}
rrc->reconfig_after_reestab = false;
}
nr_rrc_ue_generate_RRCReconfigurationComplete(rrc, Srb_id, c1->choice.rrcReconfiguration->rrc_TransactionIdentifier);
} break;
case NR_DL_DCCH_MessageType__c1_PR_rrcResume:
LOG_E(NR_RRC, "Received rrcResume on DL-DCCH-Message -> Not handled\n");
break;
case NR_DL_DCCH_MessageType__c1_PR_rrcRelease:
LOG_I(NR_RRC, "[UE %ld] Received RRC Release (gNB %d)\n", rrc->ue_id, gNB_indexP);
// delay the actions 60 ms from the moment the RRCRelease message was received
UPDATE_IE(rrc->RRCRelease, dl_dcch_msg->message.choice.c1->choice.rrcRelease, NR_RRCRelease_t);
nr_timer_setup(&rrc->release_timer, 60, 10); // 10ms step
nr_timer_start(&rrc->release_timer);
break;
case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry:
LOG_I(NR_RRC, "Received Capability Enquiry (gNB %d)\n", gNB_indexP);
nr_rrc_ue_process_ueCapabilityEnquiry(rrc, c1->choice.ueCapabilityEnquiry);
break;
case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment:
LOG_I(NR_RRC, "Logical Channel DL-DCCH (SRB1), Received RRCReestablishment\n");
nr_rrc_ue_process_rrcReestablishment(rrc,
gNB_indexP,
c1->choice.rrcReestablishment,
Srb_id,
Buffer,
Buffer_size,
msg_integrity);
break;
case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: {
NR_DLInformationTransfer_t *dlInfo = c1->choice.dlInformationTransfer;
if (dlInfo->criticalExtensions.present == NR_DLInformationTransfer__criticalExtensions_PR_dlInformationTransfer) {
NR_DLInformationTransfer_IEs_t *dlInfo_IE = dlInfo->criticalExtensions.choice.dlInformationTransfer;
/* This message hold a dedicated info NAS payload, forward it to NAS */
NR_DedicatedNAS_Message_t *dedicatedNAS_Message = dlInfo_IE->dedicatedNAS_Message;
if (dedicatedNAS_Message) {
MessageDef *ittiMsg = itti_alloc_new_message(TASK_RRC_NRUE, rrc->ue_id, NAS_DOWNLINK_DATA_IND);
dl_info_transfer_ind_t *msg = &NAS_DOWNLINK_DATA_IND(ittiMsg);
msg->UEid = rrc->ue_id;
msg->nasMsg.length = dedicatedNAS_Message->size;
msg->nasMsg.nas_data = malloc(msg->nasMsg.length);
memcpy(msg->nasMsg.nas_data, dedicatedNAS_Message->buf, msg->nasMsg.length);
itti_send_msg_to_task(TASK_NAS_NRUE, rrc->ue_id, ittiMsg);
}
}
} break;
case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand:
case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16:
case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16:
case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16:
case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16:
case NR_DL_DCCH_MessageType__c1_PR_spare3:
case NR_DL_DCCH_MessageType__c1_PR_spare2:
case NR_DL_DCCH_MessageType__c1_PR_spare1:
case NR_DL_DCCH_MessageType__c1_PR_counterCheck:
break;
case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand:
LOG_I(NR_RRC, "Received securityModeCommand (gNB %d)\n", gNB_indexP);
nr_rrc_ue_process_securityModeCommand(rrc, c1->choice.securityModeCommand, Srb_id, Buffer, Buffer_size, msg_integrity);
break;
}
} break;
default:
break;
}
// release memory allocation
SEQUENCE_free(&asn_DEF_NR_DL_DCCH_Message, dl_dcch_msg, ASFM_FREE_EVERYTHING);
return 0;
}
/** @brief Encode NAS in ULInformationTransfer, submit to PDCP on SRB2 if established else SRB1. */
static void nr_rrc_ue_send_ul_information_transfer_nas(NR_UE_RRC_INST_t *rrc, uint32_t nas_length, uint8_t *nas_pdu)
{
uint8_t *buffer = NULL;
const int enc_bytes = do_NR_ULInformationTransfer(&buffer, nas_length, nas_pdu);
const rb_id_t srb_id = rrc->Srb[2] == RB_ESTABLISHED ? 2 : 1;
LOG_D(NR_RRC,
"[UE %ld] PDCP_DATA_REQ ULInformationTransfer (NAS %u B) -> SRB%d encoded %d B\n",
rrc->ue_id,
nas_length,
(int)srb_id,
enc_bytes);
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, enc_bytes, buffer, deliver_pdu_srb_rlc, NULL);
free(buffer);
}
static void apply_ema(val_init_t *vi_rsrp_dBm, float filter_coeff_rsrp, int rsrp_dBm)
{
int *quant = &vi_rsrp_dBm->val;
bool *meas_init = &vi_rsrp_dBm->init;
float coef = *meas_init ? filter_coeff_rsrp : 1.0f; // if not init, first measurement gets full weight
*quant = (1.0f - coef) * (*quant) + coef * rsrp_dBm;
*meas_init = true;
}
void nr_ue_meas_filtering(rrcPerNB_t *rrc, meas_t *meas_cell, uint16_t Nid_cell, bool csi_meas, int rsrp_dBm)
{
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
if (meas_cell->Nid_cell != Nid_cell) {
meas_cell->ss_rsrp_dBm.init = false;
meas_cell->csi_rsrp_dBm.init = false;
}
meas_cell->Nid_cell = Nid_cell;
if (csi_meas)
apply_ema(&meas_cell->csi_rsrp_dBm, l3_measurements->csi_RS_filter_coeff_rsrp, rsrp_dBm);
else
apply_ema(&meas_cell->ss_rsrp_dBm, l3_measurements->ssb_filter_coeff_rsrp, rsrp_dBm);
}
static long get_measurement_report_interval_ms(NR_ReportInterval_t interval)
{
switch (interval) {
case NR_ReportInterval_ms120:
return 120;
case NR_ReportInterval_ms240:
return 240;
case NR_ReportInterval_ms480:
return 480;
case NR_ReportInterval_ms640:
return 640;
case NR_ReportInterval_ms1024:
return 1024;
case NR_ReportInterval_ms2048:
return 2048;
case NR_ReportInterval_ms5120:
return 5120;
case NR_ReportInterval_ms10240:
return 10240;
case NR_ReportInterval_min1:
return 60000;
case NR_ReportInterval_min6:
return 360000;
case NR_ReportInterval_min12:
return 720000;
case NR_ReportInterval_min30:
return 1800000;
default:
return 1024;
}
}
static int get_rsrp_value(const meas_t *cell)
{
if (cell->ss_rsrp_dBm.init)
return cell->ss_rsrp_dBm.val;
if (cell->csi_rsrp_dBm.init)
return cell->csi_rsrp_dBm.val;
return INT_MAX;
}
static int get_meas_id(rrcPerNB_t *rrcNB, int report_config_id)
{
for (int j = 0; j < MAX_MEAS_ID; j++) {
NR_MeasIdToAddMod_t *meas_id_toAddMod = rrcNB->MeasId[j];
if (meas_id_toAddMod && meas_id_toAddMod->reportConfigId == report_config_id)
return meas_id_toAddMod->measId;
}
return -1;
}
static void setup_meas_trigger(l3_measurements_t *l3_measurements,
NR_EventTriggerConfig_t *event_config,
int meas_id,
long trigger_quantity,
bool neighbor_cell_valid)
{
l3_measurements->trigger_to_measid = meas_id;
l3_measurements->trigger_quantity = trigger_quantity;
l3_measurements->rs_type = event_config->rsType;
l3_measurements->reports_sent = 0;
l3_measurements->max_reports =
(event_config->reportAmount == NR_EventTriggerConfig__reportAmount_infinity) ? INT_MAX : (1 << event_config->reportAmount);
l3_measurements->report_interval_ms = get_measurement_report_interval_ms(event_config->reportInterval);
l3_measurements->neighbor_cell_valid = neighbor_cell_valid;
}
static void start_meas_event(l3_measurements_t *l3_measurements,
rrcPerNB_t *rrcNB,
NR_timer_t *event_timer,
NR_EventTriggerConfig_t *event_config,
int report_config_id,
long trigger_quantity,
bool neighbor_cell_valid,
long time_to_trigger)
{
nr_timer_setup(event_timer, get_event_time_to_trigger(time_to_trigger), 10);
nr_timer_start(event_timer);
int meas_id = get_meas_id(rrcNB, report_config_id);
AssertFatal(meas_id > 0, "meas_id did not found for report_config_id %i\n", report_config_id);
setup_meas_trigger(l3_measurements, event_config, meas_id, trigger_quantity, neighbor_cell_valid);
}
static void stop_meas_event(l3_measurements_t *l3_measurements, NR_timer_t *event_timer)
{
nr_timer_stop(event_timer);
nr_timer_stop(&l3_measurements->periodic_report_timer);
l3_measurements->reports_sent = 0;
}
// TS 38.331 - 5.5.4.3 Event A2 (Serving becomes worse than threshold)
static void handle_event_a2(l3_measurements_t *l3_measurements,
rrcPerNB_t *rrcNB,
struct NR_EventTriggerConfig__eventId__eventA2 *event_A2,
NR_EventTriggerConfig_t *event_trigger_config,
long report_config_id)
{
if (event_A2->a2_Threshold.present != NR_MeasTriggerQuantity_PR_rsrp)
return;
meas_t *serving_cell = &l3_measurements->serving_cell;
int serving_cell_rsrp = get_rsrp_value(serving_cell);
if (serving_cell_rsrp == INT_MAX) {
LOG_E(NR_RRC, "There are no RSRP measurements taken for the active cell\n");
}
// TS 38.133 - Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping
int rsrp_threshold = event_A2->a2_Threshold.choice.rsrp - 157;
int rsrp_hysteresis = event_A2->hysteresis >> 1;
if (serving_cell_rsrp + rsrp_hysteresis < rsrp_threshold) {
if (!nr_timer_is_active(&l3_measurements->TA2) && (l3_measurements->reports_sent == 0)) {
start_meas_event(l3_measurements,
rrcNB,
&l3_measurements->TA2,
event_trigger_config,
report_config_id,
event_A2->a2_Threshold.present,
false,
event_A2->timeToTrigger);
LOG_W(NR_RRC,
"(active_cell_rsrp) %i + (rsrp_hysteresis) %i < (rsrp_threshold) %i\n",
serving_cell_rsrp,
rsrp_hysteresis,
rsrp_threshold);
}
} else if (nr_timer_is_active(&l3_measurements->TA2) && (serving_cell_rsrp - rsrp_hysteresis > rsrp_threshold)) {
stop_meas_event(l3_measurements, &l3_measurements->TA2);
}
}
// TS 38.331 - 5.5.4.4 Event A3 (Neighbour becomes offset better than SpCell)
static void handle_event_a3(l3_measurements_t *l3_measurements,
rrcPerNB_t *rrcNB,
struct NR_EventTriggerConfig__eventId__eventA3 *event_A3,
NR_EventTriggerConfig_t *event_trigger_config,
long report_config_id)
{
if (event_A3->a3_Offset.present != NR_MeasTriggerQuantityOffset_PR_rsrp)
return;
meas_t *serving_cell = &l3_measurements->serving_cell;
int serving_cell_rsrp = get_rsrp_value(serving_cell);
if (serving_cell_rsrp == INT_MAX) {
LOG_D(NR_RRC, "There are no RSRP measurements taken for the serving cell\n");
return;
}
int rsrp_offset = event_A3->a3_Offset.choice.rsrp >> 1;
int rsrp_hysteresis = event_A3->hysteresis;
// Check all neighboring cells for Event A3 condition
bool entry_cond_met = false;
bool above_leaving_threshold = false;
int entry_neighbor_rsrp = INT_MIN;
for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) {
meas_t *neighboring_cell = &l3_measurements->neighboring_cell[i];
int neighboring_cell_rsrp = get_rsrp_value(neighboring_cell);
if (neighboring_cell_rsrp == INT_MAX) {
LOG_D(NR_RRC, "There are no RSRP measurements taken for the neighboring cell %d\n", i);
neighboring_cell_rsrp = INT_MIN;
}
// Check entry condition
if (neighboring_cell_rsrp > serving_cell_rsrp + rsrp_offset + rsrp_hysteresis) {
entry_cond_met = true;
entry_neighbor_rsrp = neighboring_cell_rsrp;
}
// Check if any neighbor is still above leaving threshold
if (neighboring_cell_rsrp >= serving_cell_rsrp + rsrp_offset - rsrp_hysteresis) {
above_leaving_threshold = true;
}
}
// Trigger event if any neighbor meets entry condition
if (entry_cond_met && !nr_timer_is_active(&l3_measurements->TA3) && (l3_measurements->reports_sent == 0)) {
start_meas_event(l3_measurements,
rrcNB,
&l3_measurements->TA3,
event_trigger_config,
report_config_id,
NR_MeasTriggerQuantityOffset_PR_rsrp,
true,
event_A3->timeToTrigger);
LOG_W(NR_RRC,
"(neighboring_cell_rsrp) %i > (serving_cell_rsrp %i) + (rsrp_offset) %i + (rsrp_hysteresis) %i\n",
entry_neighbor_rsrp,
serving_cell_rsrp,
rsrp_offset,
rsrp_hysteresis);
}
// Stop event if all neighbors are below leaving threshold
else if (nr_timer_is_active(&l3_measurements->TA3) && !above_leaving_threshold) {
stop_meas_event(l3_measurements, &l3_measurements->TA3);
}
}
static void nr_ue_check_meas_report(NR_UE_RRC_INST_t *rrc, const uint8_t gnb_index)
{
rrcPerNB_t *rrcNB = rrc->perNB + gnb_index;
l3_measurements_t *l3_measurements = &rrcNB->l3_measurements;
for (int i = 0; i < MAX_MEAS_CONFIG; i++) {
NR_ReportConfigToAddMod_t *report_config = rrcNB->ReportConfig[i];
if (report_config == NULL)
continue;
if (report_config->reportConfig.present != NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR)
continue;
NR_ReportConfigNR_t *report_config_nr = report_config->reportConfig.choice.reportConfigNR;
if (report_config_nr->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered)
continue;
NR_EventTriggerConfig_t *event_trigger_config = report_config_nr->reportType.choice.eventTriggered;
switch (event_trigger_config->eventId.present) {
case NR_EventTriggerConfig__eventId_PR_eventA2:
handle_event_a2(l3_measurements,
rrcNB,
event_trigger_config->eventId.choice.eventA2,
event_trigger_config,
report_config->reportConfigId);
break;
case NR_EventTriggerConfig__eventId_PR_eventA3:
handle_event_a3(l3_measurements,
rrcNB,
event_trigger_config->eventId.choice.eventA3,
event_trigger_config,
report_config->reportConfigId);
break;
default:
break;
}
}
}
static void nr_rrc_handle_ra_indication(NR_UE_RRC_INST_t *rrc, bool ra_succeeded, int gNB_index)
{
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
if (ra_succeeded && nr_timer_is_active(&timers->T304)) {
// successful Random Access procedure triggered by reconfigurationWithSync
// procedures described in 5.3.5.3 of 38.331 when
// reconfigurationWithSync is included in spCellConfig
nr_timer_stop(&timers->T304);
// TODO apply the parts of the CQI reporting configuration,
// the scheduling request configuration and the sounding RS
// configuration that do not require the UE to know the SFN of the respective target SpCell
// TODO apply the parts of the measurement and the radio resource configuration
// that require the UE to know the SFN of the respective target SpCell
// (not sure what to do for these two points, probably not relevant for our implementation)
// if T390 is running stop timer T390 for all access categories
if (nr_timer_is_active(&timers->T390)) {
nr_timer_stop(&timers->T390);
// TODO perform the actions as specified in 5.3.14.4.
}
if (rrc->sched_reconfsync_sib1) {
rrc->sched_reconfsync_sib1 = false;
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
SI_info->sib_pending = true;
nr_mac_rrc_message_t sib_msg = {0};
sib_msg.payload_type = NR_MAC_RRC_SCHED_SIB;
sib_msg.payload.sched_sib.get_sib = 1;
nr_rrc_send_msg_to_mac(rrc, &sib_msg);
}
} else if (!ra_succeeded) {
// upon random access problem indication from MCG MAC
// while neither T300, T301, T304, T311 nor T319 are running
// consider radio link failure to be detected
if (!nr_timer_is_active(&timers->T300)
&& !nr_timer_is_active(&timers->T301)
&& !nr_timer_is_active(&timers->T304)
&& !nr_timer_is_active(&timers->T311)
&& !nr_timer_is_active(&timers->T319))
handle_rlf_detection(rrc);
}
}
static void nr_rrc_handle_meas_indication(NR_UE_RRC_INST_t *rrc, NRRrcMacMeasDataInd *meas_ind)
{
rrcPerNB_t *rrcNB = rrc->perNB + meas_ind->gnb_index;
l3_measurements_t *l3_measurements = &rrcNB->l3_measurements;
meas_t *meas_cell = NULL;
if (meas_ind->is_neighboring_cell) {
uint16_t target_nid_cell = meas_ind->Nid_cell;
for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) {
if (l3_measurements->neighboring_cell[i].Nid_cell == target_nid_cell) {
meas_cell = &l3_measurements->neighboring_cell[i];
break;
}
}
if (!meas_cell) {
for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) {
if (!l3_measurements->neighboring_cell[i].ss_rsrp_dBm.init && !l3_measurements->neighboring_cell[i].csi_rsrp_dBm.init) {
meas_cell = &l3_measurements->neighboring_cell[i];
break;
}
}
}
} else {
meas_cell = &l3_measurements->serving_cell;
}
if (!meas_cell) {
LOG_E(NR_RRC, "meas_cell not found!\n");
return;
}
if (meas_ind->is_neighboring_cell && meas_ind->rsrp_dBm == INT_MAX) {
LOG_W(NR_RRC, "[Nid_cell %i] Neighboring cell not detected. L3 measurements will be reset.\n", meas_ind->Nid_cell);
meas_cell->Nid_cell = meas_ind->Nid_cell;
nr_ue_meas_reset(meas_cell, meas_ind->is_csi_meas);
} else {
LOG_D(NR_RRC,
"[%s][Nid_cell %i] Received %s measurements: RSRP = %i (dBm)\n",
meas_ind->is_neighboring_cell ? "Neighboring cell" : "Active cell",
meas_ind->Nid_cell,
meas_ind->is_csi_meas ? "CSI meas" : "SSB meas",
meas_ind->rsrp_dBm);
nr_ue_meas_filtering(rrcNB, meas_cell, meas_ind->Nid_cell, meas_ind->is_csi_meas, meas_ind->rsrp_dBm);
nr_ue_check_meas_report(rrc, meas_ind->gnb_index);
}
}
void *rrc_nrue_task(void *args_p)
{
UNUSED(args_p);
itti_mark_task_ready(TASK_RRC_NRUE);
while (1) {
rrc_nrue(NULL);
}
}
void *rrc_nrue(void *notUsed)
{
UNUSED(notUsed);
MessageDef *msg_p = NULL;
itti_receive_msg(TASK_RRC_NRUE, &msg_p);
instance_t instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
LOG_D(NR_RRC, "[UE %ld] Received %s\n", instance, ITTI_MSG_NAME(msg_p));
NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(instance);
AssertFatal(instance == rrc->ue_id, "Instance %ld received from ITTI doesn't matach with UE-ID %ld\n", instance, rrc->ue_id);
switch (ITTI_MSG_ID(msg_p)) {
case TERMINATE_MESSAGE:
LOG_W(NR_RRC, " *** Exiting RRC thread\n");
itti_exit_task();
break;
case MESSAGE_TEST:
break;
case NR_RRC_MAC_SYNC_IND: {
nr_sync_msg_t sync_msg = NR_RRC_MAC_SYNC_IND(msg_p).in_sync ? IN_SYNC : OUT_OF_SYNC;
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
handle_rlf_sync(tac, sync_msg);
} break;
case NRRRC_FRAME_PROCESS:
rrc->current_hfn = NRRRC_FRAME_PROCESS(msg_p).hfn;
rrc->current_frame = NRRRC_FRAME_PROCESS(msg_p).frame;
LOG_D(NR_RRC, "Received %s: frame %d\n", ITTI_MSG_NAME(msg_p), rrc->current_frame);
// increase the timers every 10ms (every new frame)
nr_rrc_handle_timers(rrc);
NR_UE_RRC_SI_INFO *SInfo = &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo;
nr_rrc_SI_timers(SInfo);
if (rrc->process_target_ntncfg) {
// Process target NTNCFG as the target cells timing is acquired
nr_rrc_process_ntnconfig(rrc, &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo, rrc->target_ntncfg, rrc->current_frame, true);
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
rrc->target_ntncfg = NULL;
rrc->process_target_ntncfg = false;
}
break;
case NR_RRC_MAC_VERIFY:
LOG_W(NR_RRC, "L2 verification of RRC consistency failed\n");
handle_rlf_detection(rrc);
break;
case NR_RRC_MAC_INAC_IND:
LOG_D(NR_RRC, "Received data inactivity indication from lower layers\n");
NR_Release_Cause_t release_cause = RRC_CONNECTION_FAILURE;
nr_rrc_going_to_IDLE(rrc, release_cause, NULL);
break;
case NR_RRC_RLC_MAXRTX:
// detection of RLF upon indication from RLC that the maximum number of retransmissions has been reached
LOG_W(NR_RRC,
"[UE %ld ID %d] Received indication that RLC reached max retransmissions\n",
instance,
NR_RRC_RLC_MAXRTX(msg_p).ue_id);
handle_rlf_detection(rrc);
break;
case NR_RRC_MAC_MSG3_IND:
if (NR_RRC_MAC_MSG3_IND(msg_p).prepare_payload)
nr_rrc_prepare_msg3_payload(rrc);
else
nr_rrc_handle_msg3_indication(rrc, NR_RRC_MAC_MSG3_IND(msg_p).rnti);
break;
case NR_RRC_MAC_RA_IND:
LOG_D(NR_RRC,
"[UE %ld] Received %s: RA %s\n",
rrc->ue_id,
ITTI_MSG_NAME(msg_p),
NR_RRC_MAC_RA_IND(msg_p).RA_succeeded ? "successful" : "failed");
nr_rrc_handle_ra_indication(rrc, NR_RRC_MAC_RA_IND(msg_p).RA_succeeded, NR_RRC_MAC_RA_IND(msg_p).gnb_index);
break;
case NR_RRC_MAC_BCCH_DATA_IND:
LOG_D(NR_RRC, "[UE %ld] Received %s: gNB %d\n", rrc->ue_id, ITTI_MSG_NAME(msg_p), NR_RRC_MAC_BCCH_DATA_IND(msg_p).gnb_index);
NRRrcMacBcchDataInd *bcch = &NR_RRC_MAC_BCCH_DATA_IND(msg_p);
if (bcch->is_bch)
nr_rrc_ue_decode_NR_BCCH_BCH_Message(rrc, bcch->gnb_index, bcch->phycellid, bcch->ssb_arfcn, bcch->sdu, bcch->sdu_size);
else
nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(rrc, bcch->gnb_index, bcch->sdu, bcch->sdu_size, bcch->hfn, bcch->frame, bcch->slot);
break;
case NR_RRC_MAC_SBCCH_DATA_IND:
LOG_D(NR_RRC, "[UE %ld] Received %s: gNB %d\n", instance, ITTI_MSG_NAME(msg_p), NR_RRC_MAC_SBCCH_DATA_IND(msg_p).gnb_index);
NRRrcMacSBcchDataInd *sbcch = &NR_RRC_MAC_SBCCH_DATA_IND(msg_p);
nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(rrc, sbcch->sdu, sbcch->sdu_size, sbcch->rx_slss_id);
break;
case NR_RRC_MAC_MEAS_DATA_IND:
nr_rrc_handle_meas_indication(rrc, &NR_RRC_MAC_MEAS_DATA_IND(msg_p));
break;
case NR_RRC_MAC_CCCH_DATA_IND: {
NRRrcMacCcchDataInd *ind = &NR_RRC_MAC_CCCH_DATA_IND(msg_p);
nr_rrc_ue_decode_ccch(rrc, ind);
} break;
case NR_RRC_MAC_PCCH_DATA_IND: {
NRRrcMacPcchDataInd *ind = &NR_RRC_MAC_PCCH_DATA_IND(msg_p);
const byte_array_t pcch = {.len = ind->sdu_size, .buf = ind->sdu};
if (nr_rrc_ue_decode_pcch(rrc, pcch) == 1) {
LOG_I(NR_RRC, "[UE %ld] Paging match found in PagingRecordList\n", rrc->ue_id);
MessageDef *nas_msg = itti_alloc_new_message(TASK_RRC_NRUE, rrc->ue_id, NAS_PAGING_IND);
if (nas_msg != NULL) {
NAS_PAGING_IND(nas_msg).cause = AS_CONNECTION_ESTABLISH;
LOG_I(NR_RRC, "[UE %ld] Triggering Service Request after paging (cause=AS_CONNECTION_ESTABLISH)\n", rrc->ue_id);
itti_send_msg_to_task(TASK_NAS_NRUE, rrc->ue_id, nas_msg);
} else {
LOG_E(NR_RRC, "[UE %ld] Failed to allocate NAS_PAGING_IND message\n", rrc->ue_id);
}
}
} break;
case NR_RRC_DCCH_DATA_IND:
nr_rrc_ue_decode_dcch(rrc,
NR_RRC_DCCH_DATA_IND(msg_p).dcch_index,
NR_RRC_DCCH_DATA_IND(msg_p).sdu_p,
NR_RRC_DCCH_DATA_IND(msg_p).sdu_size,
NR_RRC_DCCH_DATA_IND(msg_p).gNB_index,
&NR_RRC_DCCH_DATA_IND(msg_p).msg_integrity);
/* this is allocated by itti_malloc in PDCP task (deliver_sdu_srb)
then passed to the RRC task and freed after use */
free(NR_RRC_DCCH_DATA_IND(msg_p).sdu_p);
break;
case NAS_KENB_REFRESH_REQ:
memcpy(rrc->kgnb, NAS_KENB_REFRESH_REQ(msg_p).kenb, sizeof(rrc->kgnb));
break;
case NAS_DETACH_REQ:
if (NAS_DETACH_REQ(msg_p).wait_release)
rrc->detach_after_release = true;
else {
rrc->nrRrcState = RRC_STATE_DETACH_NR;
NR_Release_Cause_t release_cause = OTHER;
nr_rrc_going_to_IDLE(rrc, release_cause, NULL);
}
break;
case NAS_UPLINK_DATA_REQ: {
ul_info_transfer_req_t *req = &NAS_UPLINK_DATA_REQ(msg_p);
/* ULInformationTransfer (TS 38.331) requires an established UL-DCCH SRB: not used for CM-IDLE initial NAS
* (that path uses RRCSetupComplete dedicatedNAS-Message (TS 38.331 §5.3.3.4, TS 33.501 §6.8.1.2.1)). */
if (rrc->Srb[1] != RB_ESTABLISHED && rrc->Srb[2] != RB_ESTABLISHED) {
LOG_W(NR_RRC,
"[UE %ld] NAS UL requested but no SRB established: dropping UL request (%u B)\n",
rrc->ue_id,
req->nasMsg.length);
free(req->nasMsg.nas_data);
break;
}
nr_rrc_ue_send_ul_information_transfer_nas(rrc, req->nasMsg.length, req->nasMsg.nas_data);
free(req->nasMsg.nas_data);
break;
}
case NAS_INITIAL_UL_TRANSFER_REQ: {
ul_info_transfer_req_t *req = &NAS_INITIAL_UL_TRANSFER_REQ(msg_p);
if (rrc->Srb[1] != RB_ESTABLISHED && rrc->Srb[2] != RB_ESTABLISHED) {
/* No UL-DCCH SRB yet: cannot use ULInformationTransfer (TS 38.331). Buffer NAS for dedicatedNAS-Message in
* RRCSetupComplete (§5.3.3.4). Typical source is Service Request from 5GMM-IDLE (TS 24.501 §5.6.1). */
free(rrc->pending_initial_nas.nas_data);
rrc->pending_initial_nas.nas_data = req->nasMsg.nas_data;
rrc->pending_initial_nas.length = req->nasMsg.length;
LOG_I(NR_RRC,
"[UE %ld] Initial NAS UL: no SRB yet; buffered %u B for RRCSetupComplete dedicatedNAS (RRC state=%d)\n",
rrc->ue_id,
req->nasMsg.length,
rrc->nrRrcState);
if (rrc->nrRrcState == RRC_STATE_IDLE_NR) {
RA_trigger_t prev_trigger = rrc->ra_trigger;
rrc->ra_trigger = RRC_CONNECTION_SETUP;
nr_rrc_ue_prepare_RRCSetupRequest(rrc);
nr_rrc_trigger_mac_ra(rrc, NR_MAC_RA_START_SETUP);
LOG_I(NR_RRC,
"[UE %ld] Triggering MAC RA for RRCSetupComplete pending NAS (prev_trigger=%d)\n",
rrc->ue_id,
prev_trigger);
}
break;
}
LOG_W(NR_RRC,
"[UE %ld] Initial NAS UL requested but SRB established: dropping request (length=%u)\n",
rrc->ue_id,
req->nasMsg.length);
free(rrc->pending_initial_nas.nas_data);
rrc->pending_initial_nas.nas_data = NULL;
rrc->pending_initial_nas.length = 0;
free(req->nasMsg.nas_data);
break;
}
case NAS_5GMM_IND: {
nas_5gmm_ind_t *req = &NAS_5GMM_IND(msg_p);
rrc->fiveG_S_TMSI = req->fiveG_STMSI;
/* Push the 5G-S-TMSI-derived UE_ID to MAC for paging PF/PO derivation */
nr_rrc_mac_config_req_paging_ue_id(rrc->ue_id, rrc->fiveG_S_TMSI);
break;
}
default:
LOG_E(NR_RRC, "[UE %ld] Received unexpected message %s\n", rrc->ue_id, ITTI_MSG_NAME(msg_p));
break;
}
LOG_D(NR_RRC, "[UE %ld] RRC Status %d\n", rrc->ue_id, rrc->nrRrcState);
int result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
return NULL;
}
void nr_rrc_ue_process_sidelink_radioResourceConfig(NR_SetupRelease_SL_ConfigDedicatedNR_r16_t *sl_ConfigDedicatedNR)
{
//process sl_CommConfig, configure MAC/PHY for transmitting SL communication (RRC_CONNECTED)
if (sl_ConfigDedicatedNR != NULL) {
switch (sl_ConfigDedicatedNR->present){
case NR_SetupRelease_SL_ConfigDedicatedNR_r16_PR_setup:
//TODO
break;
case NR_SetupRelease_SL_ConfigDedicatedNR_r16_PR_release:
break;
case NR_SetupRelease_SL_ConfigDedicatedNR_r16_PR_NOTHING:
break;
default:
break;
}
}
}
static void nr_rrc_initiate_rrcReestablishment(NR_UE_RRC_INST_t *rrc, NR_ReestablishmentCause_t cause)
{
rrc->reestablishment_cause = cause;
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
// reset timers to SIB1 as part of release of spCellConfig
// it needs to be done before handling timers
set_rlf_sib1_timers_and_constants(timers, rrc->timers_and_constants.sib1_TimersAndConstants);
// stop timer T310, if running
nr_timer_stop(&timers->T310);
// stop timer T304, if running
nr_timer_stop(&timers->T304);
// start timer T311
nr_timer_start(&timers->T311);
// suspend all RBs, except SRB0
for (int i = 1; i < 4; i++) {
if (rrc->Srb[i] == RB_ESTABLISHED) {
rrc->Srb[i] = RB_SUSPENDED;
}
}
for (int i = 1; i <= MAX_DRBS_PER_UE; i++) {
if (get_DRB_status(rrc, i) == RB_ESTABLISHED) {
set_DRB_status(rrc, i, RB_SUSPENDED);
}
}
// Free Target NTNcfg is stored
if (rrc->target_ntncfg) {
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
rrc->target_ntncfg = NULL;
rrc->process_target_ntncfg = false;
}
// release the MCG SCell(s), if configured
// no SCell configured in our implementation
// reset MAC
// release spCellConfig, if configured
// perform cell selection in accordance with the cell selection process
nr_rrc_trigger_mac_ra(rrc, NR_MAC_RA_START_REESTABLISHMENT);
}
void handle_RRCRelease(NR_UE_RRC_INST_t *rrc)
{
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
// stop timer T380, if running
nr_timer_stop(&tac->T380);
// stop timer T320, if running
nr_timer_stop(&tac->T320);
if (rrc->detach_after_release)
rrc->nrRrcState = RRC_STATE_DETACH_NR;
const struct NR_RRCRelease_IEs *rrcReleaseIEs = rrc->RRCRelease ? rrc->RRCRelease->criticalExtensions.choice.rrcRelease : NULL;
if (!rrc->as_security_activated) {
// ignore any field included in RRCRelease message except waitTime
// perform the actions upon going to RRC_IDLE as specified in 5.3.11 with the release cause 'other'
// upon which the procedure ends
NR_Release_Cause_t cause = OTHER;
nr_rrc_going_to_IDLE(rrc, cause, rrc->RRCRelease);
asn1cFreeStruc(asn_DEF_NR_RRCRelease, rrc->RRCRelease);
return;
}
bool suspend = false;
if (rrcReleaseIEs) {
if (rrcReleaseIEs->redirectedCarrierInfo)
LOG_E(NR_RRC, "redirectedCarrierInfo in RRCRelease not handled\n");
if (rrcReleaseIEs->cellReselectionPriorities)
LOG_E(NR_RRC, "cellReselectionPriorities in RRCRelease not handled\n");
if (rrcReleaseIEs->deprioritisationReq)
LOG_E(NR_RRC, "deprioritisationReq in RRCRelease not handled\n");
if (rrcReleaseIEs->suspendConfig) {
suspend = true;
// procedures to go in INACTIVE state
AssertFatal(false, "Inactive State not supported\n");
}
}
if (!suspend) {
NR_Release_Cause_t cause = OTHER;
nr_rrc_going_to_IDLE(rrc, cause, rrc->RRCRelease);
}
asn1cFreeStruc(asn_DEF_NR_RRCRelease, rrc->RRCRelease);
}
void handle_rlf_detection(NR_UE_RRC_INST_t *rrc)
{
// 5.3.10.3 in 38.331
bool srb2 = rrc->Srb[2] != RB_NOT_PRESENT;
bool any_drb = false;
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
if (rrc->status_DRBs[i] != RB_NOT_PRESENT) {
any_drb = true;
break;
}
}
if (rrc->as_security_activated && srb2 && any_drb) // initiate the connection re-establishment procedure
nr_rrc_initiate_rrcReestablishment(rrc, NR_ReestablishmentCause_otherFailure);
else {
NR_Release_Cause_t cause = rrc->as_security_activated ? RRC_CONNECTION_FAILURE : OTHER;
nr_rrc_going_to_IDLE(rrc, cause, NULL);
}
}
void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc,
NR_Release_Cause_t release_cause,
NR_RRCRelease_t *RRCRelease)
{
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
struct NR_RRCRelease_IEs *rrcReleaseIEs = RRCRelease ? RRCRelease->criticalExtensions.choice.rrcRelease : NULL;
// if going to RRC_IDLE was triggered by reception
// of the RRCRelease message including a waitTime
NR_RejectWaitTime_t *waitTime = NULL;
if (RRCRelease) {
if(rrcReleaseIEs) {
waitTime = rrcReleaseIEs->nonCriticalExtension ?
rrcReleaseIEs->nonCriticalExtension->waitTime : NULL;
if (waitTime) {
nr_timer_stop(&tac->T302); // stop 302
// start timer T302 with the value set to the waitTime
int target = *waitTime * 1000; // waitTime is in seconds
nr_timer_setup(&tac->T302, target, 10);
nr_timer_start(&tac->T302);
// TODO inform upper layers that access barring is applicable
// for all access categories except categories '0' and '2'.
// for now we just set the access barred in RRC
rrc->access_barred = true;
}
}
}
if (!waitTime) {
if (nr_timer_is_active(&tac->T302)) {
nr_timer_stop(&tac->T302);
handle_302_expired_stopped(rrc);
}
}
if (nr_timer_is_active(&tac->T390)) {
nr_timer_stop(&tac->T390);
// TODO barring alleviation as in 5.3.14.4
// not implemented
LOG_E(NR_RRC,"Go to IDLE. Barring alleviation not implemented\n");
}
if (!RRCRelease && rrc->nrRrcState == RRC_STATE_INACTIVE_NR) {
// TODO discard the cell reselection priority information provided by the cellReselectionPriorities
// cell reselection priorities not implemented yet
nr_timer_stop(&tac->T320);
}
// Stop all the timers except T302, T320 and T325
nr_timer_stop(&tac->T300);
nr_timer_stop(&tac->T301);
nr_timer_stop(&tac->T304);
nr_timer_stop(&tac->T310);
nr_timer_stop(&tac->T311);
nr_timer_stop(&tac->T319);
// discard the UE Inactive AS context
// TODO there is no inactive AS context
// release the suspendConfig
// TODO suspendConfig not handled yet
// discard the keys (only kgnb is stored)
memset(rrc->kgnb, 0, sizeof(rrc->kgnb));
rrc->integrityProtAlgorithm = 0;
rrc->cipheringAlgorithm = 0;
// release all radio resources, including release of the RLC entity,
// the MAC configuration and the associated PDCP entity
// and SDAP for all established RBs
for (int i = 1; i <= MAX_DRBS_PER_UE; i++) {
if (get_DRB_status(rrc, i) != RB_NOT_PRESENT) {
set_DRB_status(rrc, i, RB_NOT_PRESENT);
nr_pdcp_release_drb(rrc->ue_id, i);
}
}
// stop TUN threads and clean up SDAP entities
nr_sdap_delete_ue_entities(rrc->ue_id);
for (int i = 1; i < NR_NUM_SRB; i++) {
if (rrc->Srb[i] != RB_NOT_PRESENT) {
rrc->Srb[i] = RB_NOT_PRESENT;
nr_pdcp_release_srb(rrc->ue_id, i);
}
}
for (int i = 0; i < NR_MAX_NUM_LCID; i++) {
nr_rrc_release_rlc_entity(rrc, i);
}
/* TS 38.331 §5.3.11 enters RRC_IDLE with cell selection per TS 38.304 §5.2.6.
* With a normal no-redirection RRCRelease, preserve the already-acquired camped-cell context for paging. */
const bool preserve_camped_context = rrc->nrRrcState != RRC_STATE_DETACH_NR && release_cause == OTHER && RRCRelease
&& (!rrcReleaseIEs || !rrcReleaseIEs->redirectedCarrierInfo);
for (int i = 0; i < NB_CNX_UE; i++) {
rrcPerNB_t *nb = &rrc->perNB[i];
NR_UE_RRC_SI_INFO *SI_info = &nb->SInfo;
init_SI_timers(SI_info);
SI_info->sib_pending = false;
if (!preserve_camped_context)
SI_info->sib1_validity = false;
SI_info->sib2_validity = false;
SI_info->sib3_validity = false;
SI_info->sib4_validity = false;
SI_info->sib5_validity = false;
SI_info->sib6_validity = false;
SI_info->sib7_validity = false;
SI_info->sib8_validity = false;
SI_info->sib9_validity = false;
SI_info->sib10_validity = false;
SI_info->sib11_validity = false;
SI_info->sib12_validity = false;
SI_info->sib13_validity = false;
SI_info->sib14_validity = false;
SI_info->SInfo_r17.sib15_validity = false;
SI_info->SInfo_r17.sib16_validity = false;
SI_info->SInfo_r17.sib17_validity = false;
SI_info->SInfo_r17.sib18_validity = false;
SI_info->SInfo_r17.sib19_validity = false;
SI_info->SInfo_r17.sib20_validity = false;
SI_info->SInfo_r17.sib21_validity = false;
}
if (rrc->nrRrcState == RRC_STATE_DETACH_NR) {
asn1cFreeStruc(asn_DEF_NR_UE_NR_Capability, rrc->UECap.UE_NR_Capability);
asn1cFreeStruc(asn_DEF_NR_UE_TimersAndConstants, tac->sib1_TimersAndConstants);
}
// Free Target NTNcfg is stored
if (rrc->target_ntncfg) {
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
rrc->target_ntncfg = NULL;
rrc->process_target_ntncfg = false;
}
// reset MAC
NR_UE_MAC_reset_cause_t cause = GO_TO_IDLE;
if (rrc->nrRrcState == RRC_STATE_DETACH_NR)
cause = DETACH;
else if (preserve_camped_context)
cause = GO_TO_IDLE_KEEP_CAMPED;
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_RESET;
rrc_msg.payload.config_reset.cause = cause;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
// enter RRC_IDLE
LOG_I(NR_RRC, "RRC moved into IDLE state\n");
if (rrc->nrRrcState != RRC_STATE_DETACH_NR)
rrc->nrRrcState = RRC_STATE_IDLE_NR;
rrc->rnti = 0;
// Indicate the release of the RRC connection to upper layers
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_NRUE, rrc->ue_id, NR_NAS_CONN_RELEASE_IND);
NR_NAS_CONN_RELEASE_IND(msg_p).cause = release_cause;
itti_send_msg_to_task(TASK_NAS_NRUE, rrc->ue_id, msg_p);
}
void handle_302_expired_stopped(NR_UE_RRC_INST_t *rrc)
{
// for each Access Category for which T390 (TODO not implemented) is not running
// consider the barring for this Access Category to be alleviated
rrc->access_barred = false;
}
void handle_t300_expiry(NR_UE_RRC_INST_t *rrc)
{
rrc->ra_trigger = RRC_CONNECTION_SETUP;
nr_rrc_ue_prepare_RRCSetupRequest(rrc);
nr_rrc_trigger_mac_ra(rrc, NR_MAC_RA_START_T300);
// TODO handle connEstFailureControl
// TODO inform upper layers about the failure to establish the RRC connection
}
void handle_t430_expiry(NR_UE_RRC_INST_t *rrc)
{
// SPEC 38.331 section 5.2.2.6
// Reacquire SIB19 after T430 expiry
for (int i = 0; i < NB_CNX_UE; i++) {
rrcPerNB_t *nb = &rrc->perNB[i];
NR_UE_RRC_SI_INFO *SI_info = &nb->SInfo;
SI_info->SInfo_r17.sib19_validity = false;
}
// Indicate MAC that UL SYNC is LOST
NR_UE_MAC_reset_cause_t cause = UL_SYNC_LOST_T430_EXPIRED;
nr_mac_rrc_message_t rrc_msg = {0};
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_RESET;
rrc_msg.payload.config_reset.cause = cause;
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
//This calls the sidelink preconf message after RRC, MAC instances are created.
void start_sidelink(int instance)
{
NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(instance);
if (get_softmodem_params()->sl_mode == 2) {
//Process the Sidelink Preconfiguration
rrc_ue_process_sidelink_Preconfiguration(rrc, get_softmodem_params()->sync_ref);
}
}
void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf)
{
NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(instance);
rrc->mac_input_nf = mac_input_nf;
}
void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id)
{
uint8_t buffer[NR_RRC_BUF_SIZE];
l3_measurements_t *l3m = &rrc->l3_measurements;
int rsrp_dBm = l3m->rs_type == NR_NR_RS_Type_ssb ? l3m->serving_cell.ss_rsrp_dBm.val : l3m->serving_cell.csi_rsrp_dBm.val;
int rsrp_index = get_rsrp_index(rsrp_dBm);
int neighbor_rsrp_dBm =
l3m->rs_type == NR_NR_RS_Type_ssb ? l3m->neighboring_cell[0].ss_rsrp_dBm.val : l3m->neighboring_cell[0].csi_rsrp_dBm.val;
int neighbor_rsrp_index = get_rsrp_index(neighbor_rsrp_dBm);
uint8_t size = do_nrMeasurementReport_SA(l3m->trigger_to_measid,
l3m->trigger_quantity,
l3m->rs_type,
l3m->serving_cell.Nid_cell,
rsrp_index,
l3m->neighbor_cell_valid,
l3m->neighboring_cell[0].Nid_cell,
neighbor_rsrp_index,
buffer,
sizeof(buffer));
int srb_id = 1; // possibly TODO in SRB3 in some cases
nr_pdcp_data_req_srb(ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL);
}