rrc_gNB.c 115 KB
Newer Older
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file rrc_gNB.c
 * \brief rrc procedures for gNB
 * \author Navid Nikaein and  Raymond Knopp , WEI-TAI CHEN
 * \date 2011 - 2014 , 2018
 * \version 1.0
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
27 28
 * \company Eurecom, NTUST
 * \email: navid.nikaein@eurecom.fr and raymond.knopp@eurecom.fr, kroempa@gmail.com
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
29
 */
30
#define RRC_GNB_C
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
31 32
#define RRC_GNB_C

Laurent THOMAS's avatar
Laurent THOMAS committed
33 34 35 36
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

37
#include "nr_rrc_config.h"
38 39
#include "nr_rrc_defs.h"
#include "nr_rrc_extern.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
40 41
#include "assertions.h"
#include "common/ran_context.h"
42
#include "oai_asn1.h"
43
#include "rrc_gNB_radio_bearers.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
44 45

#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
46
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
Raymond Knopp's avatar
Raymond Knopp committed
47
#include "common/utils/LOG/log.h"
48
#include "RRC/NR/MESSAGES/asn1_msg.h"
49
#include "openair2/E1AP/e1ap_asnc.h"
50 51 52 53

#include "NR_BCCH-BCH-Message.h"
#include "NR_UL-DCCH-Message.h"
#include "NR_DL-DCCH-Message.h"
54 55 56 57 58 59
#include "NR_DL-CCCH-Message.h"
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCReject.h"
#include "NR_RejectWaitTime.h"
#include "NR_RRCSetup.h"

60
#include "NR_CellGroupConfig.h"
61
#include "NR_MeasResults.h"
62 63
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCSetupRequest-IEs.h"
Xue Song's avatar
Xue Song committed
64
#include "NR_RRCSetupComplete-IEs.h"
65
#include "NR_RRCReestablishmentRequest-IEs.h"
66
#include "NR_MIB.h"
67 68
#include "uper_encoder.h"
#include "uper_decoder.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
69

70
#include "common/platform_types.h"
Raymond Knopp's avatar
Raymond Knopp committed
71
#include "common/utils/LOG/vcd_signal_dumper.h"
72

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
73 74 75 76 77
#include "T.h"

#include "RRC/NAS/nas_config.h"
#include "RRC/NAS/rb_config.h"

mir's avatar
mir committed
78
#include "openair3/SECU/secu_defs.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
79

Xue Song's avatar
Xue Song committed
80
#include "rrc_gNB_NGAP.h"
81
#include "rrc_gNB_du.h"
Xue Song's avatar
Xue Song committed
82

83
#include "rrc_gNB_GTPV1U.h"
84

85
#include "nr_pdcp/nr_pdcp_entity.h"
86
#include "nr_pdcp/nr_pdcp_oai_api.h"
87

88
#include "intertask_interface.h"
89
#include "SIMULATION/TOOLS/sim.h" // for taus
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
90

91
#include "executables/softmodem-common.h"
laurent's avatar
laurent committed
92
#include <openair2/RRC/NR/rrc_gNB_UE_context.h>
Laurent's avatar
Laurent committed
93
#include <openair2/X2AP/x2ap_eNB.h>
mir's avatar
mir committed
94
#include <openair3/SECU/key_nas_deriver.h>
95
#include <openair3/ocp-gtpu/gtp_itf.h>
96
#include <openair2/RRC/NR/nr_rrc_proto.h>
97
#include "openair2/F1AP/f1ap_common.h"
98
#include "openair2/F1AP/f1ap_ids.h"
99
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
100
#include "openair2/E1AP/e1ap.h"
101
#include "cucp_cuup_if.h"
102

Xue Song's avatar
Xue Song committed
103 104
#include "BIT_STRING.h"
#include "assertions.h"
105

106 107 108 109
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
#endif

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
110 111 112 113
//#define XER_PRINT

extern RAN_CONTEXT_t RC;

114
mui_t rrc_gNB_mui = 0;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
115

Robert Schmidt's avatar
Robert Schmidt committed
116 117 118 119 120 121 122 123 124 125
// the assoc_id might be 0 (if the DU goes offline). Below helper macro to
// print an error and return from the function in that case
#define RETURN_IF_INVALID_ASSOC_ID(ue_data)                                \
  {                                                                        \
    if (ue_data.du_assoc_id == 0) {                                        \
      LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \
      return;                                                              \
    }                                                                      \
  }

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
126 127
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///
luis_pereira87's avatar
luis_pereira87 committed
128

129 130 131 132 133 134 135 136
static void clear_nas_pdu(ngap_pdu_t *pdu)
{
  DevAssert(pdu != NULL);
  free(pdu->buffer); // does nothing if NULL
  pdu->buffer = NULL;
  pdu->length = 0;
}

137 138
static void freeDRBlist(NR_DRB_ToAddModList_t *list)
{
laurent's avatar
laurent committed
139
  //ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, list);
140 141
  return;
}
142

143 144 145
typedef struct deliver_dl_rrc_message_data_s {
  const gNB_RRC_INST *rrc;
  f1ap_dl_rrc_message_t *dl_rrc;
Robert Schmidt's avatar
Robert Schmidt committed
146
  sctp_assoc_t assoc_id;
147 148 149 150 151 152 153 154
} deliver_dl_rrc_message_data_t;
static void rrc_deliver_dl_rrc_message(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
  DevAssert(deliver_pdu_data != NULL);
  deliver_dl_rrc_message_data_t *data = (deliver_dl_rrc_message_data_t *)deliver_pdu_data;
  data->dl_rrc->rrc_container = (uint8_t *)buf;
  data->dl_rrc->rrc_container_length = size;
  DevAssert(data->dl_rrc->srb_id == srb_id);
Robert Schmidt's avatar
Robert Schmidt committed
155
  data->rrc->mac_rrc.dl_rrc_message_transfer(data->assoc_id, data->dl_rrc);
156 157
}

158 159 160 161 162
void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc,
                                           const gNB_RRC_UE_t *ue_p,
                                           uint8_t srb_id,
                                           const uint8_t *buffer,
                                           int size)
163 164
{
  DevAssert(size > 0);
165
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
166
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
167
  f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, .srb_id = srb_id};
168 169 170 171 172 173 174 175
  deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc, .assoc_id = ue_data.du_assoc_id};
  nr_pdcp_data_req_srb(ue_p->rrc_ue_id,
                       srb_id,
                       rrc_gNB_mui++,
                       size,
                       (unsigned char *const)buffer,
                       rrc_deliver_dl_rrc_message,
                       &data);
176
}
177

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
static int get_dl_band(const f1ap_served_cell_info_t *cell_info)
{
  return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.dl_freqinfo.band;
}

static int get_ssb_scs(const f1ap_served_cell_info_t *cell_info)
{
  return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs;
}

static int get_dl_arfcn(const f1ap_served_cell_info_t *cell_info)
{
  return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.arfcn : cell_info->fdd.dl_freqinfo.arfcn;
}

193 194 195 196 197 198 199 200
static int get_dl_bw(const f1ap_served_cell_info_t *cell_info)
{
  return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.nrb : cell_info->fdd.dl_tbw.nrb;
}

static int get_ssb_arfcn(const f1ap_served_cell_info_t *cell_info, const NR_MIB_t *mib, const NR_SIB1_t *sib1)
{
  DevAssert(cell_info != NULL && sib1 != NULL && mib != NULL);
201 202 203 204 205 206 207
  const NR_FrequencyInfoDL_SIB_t *freq_info = &sib1->servingCellConfigCommon->downlinkConfigCommon.frequencyInfoDL;
  AssertFatal(freq_info->scs_SpecificCarrierList.list.count == 1,
              "cannot handle more than one carrier, but has %d\n",
              freq_info->scs_SpecificCarrierList.list.count);
  AssertFatal(freq_info->scs_SpecificCarrierList.list.array[0]->offsetToCarrier == 0,
              "cannot handle offsetToCarrier != 0, but is %ld\n",
              freq_info->scs_SpecificCarrierList.list.array[0]->offsetToCarrier);
208 209 210 211 212 213

  long offsetToPointA = freq_info->offsetToPointA;
  long kssb = mib->ssb_SubcarrierOffset;
  uint32_t dl_arfcn = get_dl_arfcn(cell_info);
  int scs = get_ssb_scs(cell_info);
  int band = get_dl_band(cell_info);
214 215 216 217 218 219
  uint64_t freqpointa = from_nrarfcn(band, scs, dl_arfcn);
  uint64_t freqssb = 0;
  // 3GPP TS 38.211 sections 7.4.3.1 and 4.4.4.2
  // for FR1 offsetToPointA and k_SSB are expressed in terms of 15 kHz SCS
  // for FR2 offsetToPointA is expressed in terms of 60 kHz SCS and k_SSB expressed in terms of the subcarrier spacing provided
  // by the higher-layer parameter subCarrierSpacingCommon
luis_pereira87's avatar
luis_pereira87 committed
220 221 222
  // FR1 includes frequency bands from 410 MHz (ARFCN 82000) to 7125 MHz (ARFCN 875000)
  // FR2 includes frequency bands from 24.25 GHz (ARFCN 2016667) to 71.0 GHz (ARFCN 2795832)
  if (dl_arfcn >= 82000 && dl_arfcn < 875000)
223
    freqssb = freqpointa + 15000 * (offsetToPointA * 12 + kssb)  + 10ll * 12 * (1 << scs) * 15000;
luis_pereira87's avatar
luis_pereira87 committed
224
  else if (dl_arfcn >= 2016667 && dl_arfcn < 2795832)
225
    freqssb = freqpointa + 60000 * offsetToPointA * 12 + (1 << scs) * 15000 * (kssb  + 10ll * 12);
luis_pereira87's avatar
luis_pereira87 committed
226 227 228
  else
    AssertFatal(false, "Invalid absoluteFrequencyPointA: %d\n", dl_arfcn);

229 230 231 232
  int bw_index = get_supported_band_index(scs, band, get_dl_bw(cell_info));
  int band_size_hz = get_supported_bw_mhz(band > 256 ? FR2 : FR1, bw_index) * 1000 * 1000;
  uint32_t ssb_arfcn = to_nrarfcn(band, freqssb, scs, band_size_hz);

233 234 235 236 237 238 239 240 241 242 243
  LOG_D(NR_RRC,
        "freqpointa %ld Hz/%d offsetToPointA %ld kssb %ld scs %d band %d band_size_hz %d freqssb %ld Hz/%d\n",
        freqpointa,
        dl_arfcn,
        offsetToPointA,
        kssb,
        scs,
        band,
        band_size_hz,
        freqssb,
        ssb_arfcn);
244 245 246 247 248 249 250 251 252 253

  if (RC.nrmac) {
    // debugging: let's test this is the correct ARFCN
    // in the CU, we have no access to the SSB ARFCN and therefore need to
    // compute it ourselves. If we are running in monolithic, though, we have
    // access to the MAC structures and hence can read and compare to the
    // original SSB ARFCN. If the below creates problems, it can safely be
    // taken out (but the reestablishment will likely not work).
    const NR_ServingCellConfigCommon_t *scc = RC.nrmac[0]->common_channels[0].ServingCellConfigCommon;
    uint32_t scc_ssb_arfcn = *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB;
254 255 256 257 258
    AssertFatal(ssb_arfcn == scc_ssb_arfcn,
                "mismatch of SCC SSB ARFCN original %d vs. computed %d! Note: you can remove this Assert, the gNB might run but UE "
                "connection instable\n",
                scc_ssb_arfcn,
                ssb_arfcn);
259 260 261 262 263
  }

  return ssb_arfcn;
}

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
264 265 266
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///

267
static void init_NR_SI(gNB_RRC_INST *rrc)
268
{
269 270 271
  if (!NODE_IS_DU(rrc->node_type)) {
    rrc->carrier.SIB23 = (uint8_t *) malloc16(100);
    AssertFatal(rrc->carrier.SIB23 != NULL, "cannot allocate memory for SIB");
272
    rrc->carrier.sizeof_SIB23 = do_SIB23_NR(&rrc->carrier);
273 274 275
    LOG_I(NR_RRC,"do_SIB23_NR, size %d \n ", rrc->carrier.sizeof_SIB23);
    AssertFatal(rrc->carrier.sizeof_SIB23 != 255,"FATAL, RC.nrrrc[mod].carrier[CC_id].sizeof_SIB23 == 255");
  }
276

277
  if (get_softmodem_params()->phy_test > 0 || get_softmodem_params()->do_ra > 0) {
278
    AssertFatal(NODE_IS_MONOLITHIC(rrc->node_type), "phy_test and do_ra only work in monolithic\n");
279
    rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_allocate_new_ue_context(rrc);
280 281
    gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
    UE->spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig));
282
    UE->spCellConfig->spCellConfigDedicated = RC.nrmac[0]->common_channels[0].pre_ServingCellConfig;
283 284 285
    LOG_I(NR_RRC,"Adding new user (%p)\n",ue_context_p);
    if (!NODE_IS_CU(RC.nrrrc[0]->node_type)) {
      rrc_add_nsa_user(rrc,ue_context_p,NULL);
286
    }
287
  }
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
288
}
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
289

290
static void rrc_gNB_CU_DU_init(gNB_RRC_INST *rrc)
291 292
{
  switch (rrc->node_type) {
293 294 295 296
    case ngran_gNB_CUCP:
      mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
      cucp_cuup_message_transfer_e1ap_init(rrc);
      break;
297 298
    case ngran_gNB_CU:
      mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
299
      cucp_cuup_message_transfer_direct_init(rrc);
300
      break;
301 302 303 304
    case ngran_gNB:
      mac_rrc_dl_direct_init(&rrc->mac_rrc);
      cucp_cuup_message_transfer_direct_init(rrc);
       break;
305 306 307 308 309 310 311 312 313
    case ngran_gNB_DU:
      /* silently drop this, as we currently still need the RRC at the DU. As
       * soon as this is not the case anymore, we can add the AssertFatal() */
      //AssertFatal(1==0,"nothing to do for DU\n");
      break;
    default:
      AssertFatal(0 == 1, "Unknown node type %d\n", rrc->node_type);
      break;
  }
314
  cu_init_f1_ue_data();
315 316
}

317
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration)
318
{
319
  AssertFatal(rrc != NULL, "RC.nrrrc not initialized!");
320
  AssertFatal(MAX_MOBILES_PER_GNB < (module_id_t)0xFFFFFFFFFFFFFFFF, " variable overflow");
321
  AssertFatal(configuration!=NULL,"configuration input is null\n");
322
  rrc->module_id = 0;
323
  rrc_gNB_CU_DU_init(rrc);
324
  uid_linear_allocator_init(&rrc->uid_allocator);
325
  RB_INIT(&rrc->rrc_ue_head);
326
  RB_INIT(&rrc->cuups);
Robert Schmidt's avatar
Robert Schmidt committed
327
  RB_INIT(&rrc->dus);
328
  rrc->configuration = *configuration;
329
   /// System Information INIT
330
  init_NR_SI(rrc);
331 332
  return;
} // END openair_rrc_gNB_configuration
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
333

334 335
static void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_addition_req_t *m)
{
336 337 338 339 340 341 342
  struct NR_CG_ConfigInfo *cg_configinfo = NULL;
  asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                            &asn_DEF_NR_CG_ConfigInfo,
                            (void **)&cg_configinfo,
                            (uint8_t *)m->rrc_buffer,
                            (int) m->rrc_buffer_size);//m->rrc_buffer_size);
  gNB_RRC_INST         *rrc=RC.nrrrc[gnb_mod_idP];
343

344 345 346 347 348 349
  if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
    AssertFatal(1==0,"NR_UL_DCCH_MESSAGE decode error\n");
    // free the memory
    SEQUENCE_free(&asn_DEF_NR_CG_ConfigInfo, cg_configinfo, 1);
    return;
  }
350

351 352 353 354 355
  xer_fprint(stdout,&asn_DEF_NR_CG_ConfigInfo, cg_configinfo);
  // recreate enough of X2 EN-DC Container
  AssertFatal(cg_configinfo->criticalExtensions.choice.c1->present == NR_CG_ConfigInfo__criticalExtensions__c1_PR_cg_ConfigInfo,
              "ueCapabilityInformation not present\n");
  parse_CG_ConfigInfo(rrc,cg_configinfo,m);
356 357
  LOG_A(NR_RRC, "Successfully parsed CG_ConfigInfo of size %zu bits. (%zu bytes)\n",
        dec_rval.consumed, (dec_rval.consumed +7/8));
358 359
}

360
//-----------------------------------------------------------------------------
361
unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
362 363
//-----------------------------------------------------------------------------
{
364 365 366 367 368 369
  static unsigned int transaction_id[NUMBER_OF_gNB_MAX] = {0};
  // used also in NGAP thread, so need thread safe operation
  unsigned int tmp = __atomic_add_fetch(&transaction_id[gnb_mod_idP], 1, __ATOMIC_SEQ_CST);
  tmp %= NR_RRC_TRANSACTION_IDENTIFIER_NUMBER;
  LOG_T(NR_RRC, "generated xid is %d\n", tmp);
  return tmp;
370 371
}

372 373 374 375 376 377 378
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
  if (!ue->Srb[1].Active) {
    LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
    return NULL;
  }
  NR_SRB_ToAddModList_t *list = CALLOC(sizeof(*list), 1);
379
  for (int i = 0; i < NR_NUM_SRB; i++)
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
    if (ue->Srb[i].Active) {
      asn1cSequenceAdd(list->list, NR_SRB_ToAddMod_t, srb);
      srb->srb_Identity = i;
      if (reestablish && i == 2) {
        asn1cCallocOne(srb->reestablishPDCP, NR_SRB_ToAddMod__reestablishPDCP_true);
      }
    }
  return list;
}

static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
  NR_DRB_ToAddMod_t *DRB_config = NULL;
  NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);

395
  for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
396 397 398 399 400 401 402 403 404
    if (ue->established_drbs[i].status != DRB_INACTIVE) {
      DRB_config = generateDRB_ASN1(&ue->established_drbs[i]);
      if (reestablish) {
        ue->established_drbs[i].reestablishPDCP = NR_DRB_ToAddMod__reestablishPDCP_true;
        asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
      }
      asn1cSeqAdd(&DRB_configList->list, DRB_config);
    }
  }
405 406 407 408
  if (DRB_configList->list.count == 0) {
    free(DRB_configList);
    return NULL;
  }
409 410 411
  return DRB_configList;
}

laurent's avatar
laurent committed
412 413 414 415 416 417
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
{
  if (l) {
    for (int i = 0; i < l->list.count; i++)
      free(l->list.array[i]);
    free(l);
laurent's avatar
laurent committed
418 419
  } else
    LOG_E(NR_RRC, "Call free SRB list on NULL pointer\n");
laurent's avatar
laurent committed
420 421
}

422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
static void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
{
  AssertFatal(srb_id == 1 || srb_id == 2, "handling only SRB 1 or 2\n");
  if (UE->Srb[srb_id].Active == 1) {
    LOG_W(RRC, "UE %d SRB %d already activated\n", UE->rrc_ue_id, srb_id);
    return;
  }
  LOG_I(RRC, "activate SRB %d of UE %d\n", srb_id, UE->rrc_ue_id);
  UE->Srb[srb_id].Active = 1;

  NR_SRB_ToAddModList_t *list = CALLOC(sizeof(*list), 1);
  asn1cSequenceAdd(list->list, NR_SRB_ToAddMod_t, srb);
  srb->srb_Identity = srb_id;

  if (srb_id == 1) {
    nr_pdcp_add_srbs(true, UE->rrc_ue_id, list, 0, NULL, NULL);
  } else {
439 440
    uint8_t kRRCenc[NR_K_KEY_SIZE] = {0};
    uint8_t kRRCint[NR_K_KEY_SIZE] = {0};
441 442 443 444 445 446 447 448 449 450 451 452 453
    nr_derive_key(RRC_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, kRRCenc);
    nr_derive_key(RRC_INT_ALG, UE->integrity_algorithm, UE->kgnb, kRRCint);

    nr_pdcp_add_srbs(true,
                     UE->rrc_ue_id,
                     list,
                     (UE->integrity_algorithm << 4) | UE->ciphering_algorithm,
                     kRRCenc,
                     kRRCint);
  }
  freeSRBlist(list);
}

454
//-----------------------------------------------------------------------------
455 456
static void rrc_gNB_generate_RRCSetup(instance_t instance,
                                      rnti_t rnti,
457 458
                                      rrc_gNB_ue_context_t *const ue_context_pP,
                                      const uint8_t *masterCellGroup,
459
                                      int masterCellGroup_len)
460 461
//-----------------------------------------------------------------------------
{
462
  LOG_I(NR_RRC, "rrc_gNB_generate_RRCSetup for RNTI %04x\n", rnti);
463 464

  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
465
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
466
  unsigned char buf[1024];
467 468
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(instance);
  ue_p->xids[xid] = RRC_SETUP;
469
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
laurent's avatar
laurent committed
470 471

  int size = do_RRCSetup(ue_context_pP, buf, xid, masterCellGroup, masterCellGroup_len, &rrc->configuration, SRBs);
472
  AssertFatal(size > 0, "do_RRCSetup failed\n");
473
  AssertFatal(size <= 1024, "memory corruption\n");
474

475
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Setup\n");
laurent's avatar
laurent committed
476
  freeSRBlist(SRBs);
477
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
478
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
479
  f1ap_dl_rrc_message_t dl_rrc = {
480
    .gNB_CU_ue_id = ue_p->rrc_ue_id,
481
    .gNB_DU_ue_id = ue_data.secondary_ue,
482 483
    .rrc_container = buf,
    .rrc_container_length = size,
484 485
    .srb_id = CCCH
  };
Robert Schmidt's avatar
Robert Schmidt committed
486
  rrc->mac_rrc.dl_rrc_message_transfer(ue_data.du_assoc_id, &dl_rrc);
487 488
}

489
static void rrc_gNB_generate_RRCReject(module_id_t module_id, rrc_gNB_ue_context_t *const ue_context_pP)
490 491
//-----------------------------------------------------------------------------
{
492
  LOG_I(NR_RRC, "rrc_gNB_generate_RRCReject \n");
493
  gNB_RRC_INST *rrc = RC.nrrrc[module_id];
494
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
495

496
  unsigned char buf[1024];
497
  int size = do_RRCReject(buf);
498
  AssertFatal(size > 0, "do_RRCReject failed\n");
499
  AssertFatal(size <= 1024, "memory corruption\n");
500

501
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC,
502 503
              (char *)buf,
              size,
504
              "[MSG] RRCReject \n");
505
  LOG_I(NR_RRC, " [RAPROC] ue %04x Logical Channel DL-CCCH, Generating NR_RRCReject (bytes %d)\n", ue_p->rnti, size);
506

507
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
508
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
509
  f1ap_dl_rrc_message_t dl_rrc = {
510
    .gNB_CU_ue_id = ue_p->rrc_ue_id,
511
    .gNB_DU_ue_id = ue_data.secondary_ue,
512 513 514 515 516 517
    .rrc_container = buf,
    .rrc_container_length = size,
    .srb_id = CCCH,
    .execute_duplication  = 1,
    .RAT_frequency_priority_information.en_dc = 0
  };
Robert Schmidt's avatar
Robert Schmidt committed
518
  rrc->mac_rrc.dl_rrc_message_transfer(ue_data.du_assoc_id, &dl_rrc);
519 520
}

Xue Song's avatar
Xue Song committed
521 522 523 524
//-----------------------------------------------------------------------------
/*
* Process the rrc setup complete message from UE (SRB1 Active)
*/
525
static void rrc_gNB_process_RRCSetupComplete(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP, NR_RRCSetupComplete_IEs_t *rrcSetupComplete)
Xue Song's avatar
Xue Song committed
526 527
//-----------------------------------------------------------------------------
{
528
  LOG_A(NR_RRC, "UE %d Processing NR_RRCSetupComplete from UE\n", ue_context_pP->ue_context.rrc_ue_id);
laurent's avatar
tmp  
laurent committed
529 530
  ue_context_pP->ue_context.Srb[1].Active = 1;
  ue_context_pP->ue_context.Srb[2].Active = 0;
531
  ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED;
532
  AssertFatal(ctxt_pP->rntiMaybeUEid == ue_context_pP->ue_context.rrc_ue_id, "logic bug: inconsistent IDs, must use CU UE ID!\n");
Xue Song's avatar
Xue Song committed
533

534
  rrc_gNB_send_NGAP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcSetupComplete);
535 536 537 538

#ifdef E2_AGENT
  signal_rrc_state_changed_to(&ue_context_pP->ue_context, RC_SM_RRC_CONNECTED);
#endif
Xue Song's avatar
Xue Song committed
539 540
}

Xue Song's avatar
Xue Song committed
541
//-----------------------------------------------------------------------------
542
static void rrc_gNB_generate_defaultRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP)
Xue Song's avatar
Xue Song committed
543 544
//-----------------------------------------------------------------------------
{
545
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
546
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
547 548
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
  ue_p->xids[xid] = RRC_DEFAULT_RECONF;
549

550
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));
551

552
  /* Add all NAS PDUs to the list */
553 554 555 556
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
    if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
      asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
      OCTET_STRING_fromBuf(msg, (char *)ue_p->pduSession[i].param.nas_pdu.buffer, ue_p->pduSession[i].param.nas_pdu.length);
557

558 559 560 561 562
    }
    if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
      LOG_D(NR_RRC, "setting the status for the default DRB (index %d) to (%d,%s)\n", i, ue_p->pduSession[i].status, "PDU_SESSION_STATUS_DONE");
    }
563 564
  }

565 566 567
  if (ue_p->nas_pdu.length) {
    asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
    OCTET_STRING_fromBuf(msg, (char *)ue_p->nas_pdu.buffer, ue_p->nas_pdu.length);
568 569 570 571 572 573 574 575
  }

  /* If list is empty free the list and reset the address */
  if (dedicatedNAS_MessageList->list.count == 0) {
    free(dedicatedNAS_MessageList);
    dedicatedNAS_MessageList = NULL;
  }

576 577
  const nr_rrc_du_container_t *du = get_du_for_ue(rrc, ue_p->rrc_ue_id);
  DevAssert(du != NULL);
578 579 580
  f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
  int scs = get_ssb_scs(cell_info);
  int band = get_dl_band(cell_info);
581 582 583 584 585 586 587
  NR_MeasConfig_t *measconfig = NULL;
  if (du->mib != NULL && du->sib1 != NULL) {
    /* we cannot calculate the default measurement config without MIB&SIB1, as
     * we don't know the DU's SSB ARFCN */
    uint32_t ssb_arfcn = get_ssb_arfcn(cell_info, du->mib, du->sib1);
    measconfig = get_defaultMeasConfig(ssb_arfcn, band, scs);
  }
588 589
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
590

591
  uint8_t buffer[RRC_BUF_SIZE] = {0};
592
  int size = do_RRCReconfiguration(ue_p,
593
                                   buffer,
594
                                   RRC_BUF_SIZE,
595
                                   xid,
596 597
                                   SRBs,
                                   DRBs,
598 599 600 601 602
                                   NULL,
                                   NULL,
                                   measconfig,
                                   dedicatedNAS_MessageList,
                                   ue_p->masterCellGroup);
603
  AssertFatal(size > 0, "cannot encode RRCReconfiguration in %s()\n", __func__);
Robert Schmidt's avatar
Robert Schmidt committed
604
  free_defaultMeasConfig(measconfig);
605 606
  freeSRBlist(SRBs);
  freeDRBlist(DRBs);
607

608 609 610
  if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
    xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, ue_p->masterCellGroup);
  }
611

612
  clear_nas_pdu(&ue_p->nas_pdu);
613

614
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n");
615 616

  /* Free all NAS PDUs */
617 618
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
    clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
619

620
  LOG_I(NR_RRC, "UE %d: Generate RRCReconfiguration (bytes %d, xid %d)\n", ue_p->rrc_ue_id, size, xid);
621
  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
622

623
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
Xue Song's avatar
Xue Song committed
624 625
}

626
//-----------------------------------------------------------------------------
627
void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP)
628 629 630 631
//-----------------------------------------------------------------------------
{
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];

632
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
633
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
634 635 636 637 638 639 640 641 642 643 644 645 646 647
  /* hack: in many cases, we want to send a PDU session resource setup response
   * after this dedicated RRC Reconfiguration (e.g., new PDU session). However,
   * this is not always the case, e.g., when the PDU sessions came through the
   * NG initial UE context setup, in which case all PDU sessions are
   * established, and we end up here after a UE Capability Enquiry. So below,
   * check if we are currently setting up any PDU sessions, and only then, set
   * the xid to trigger a PDU session setup response. */
  ue_p->xids[xid] = RRC_DEDICATED_RECONF;
  for (int i = 0; i < ue_p->nb_of_pdusessions; ++i) {
    if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
      ue_p->xids[xid] = RRC_PDUSESSION_ESTABLISH;
      break;
    }
  }
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));

  for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
    if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
      asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
      OCTET_STRING_fromBuf(msg,
                           (char *)ue_p->pduSession[i].param.nas_pdu.buffer,
                           ue_p->pduSession[i].param.nas_pdu.length);
      asn1cSeqAdd(&dedicatedNAS_MessageList->list, msg);

      LOG_D(NR_RRC, "add NAS info with size %d (pdusession idx %d)\n", ue_p->pduSession[i].param.nas_pdu.length, i);
      ue_p->pduSession[i].xid = xid;
    }
    if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
663
    }
664
  }
665

666 667 668
  if (ue_p->nas_pdu.length) {
    asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
    OCTET_STRING_fromBuf(msg, (char *)ue_p->nas_pdu.buffer, ue_p->nas_pdu.length);
669 670 671 672 673 674 675 676
  }

  /* If list is empty free the list and reset the address */
  if (dedicatedNAS_MessageList->list.count == 0) {
    free(dedicatedNAS_MessageList);
    dedicatedNAS_MessageList = NULL;
  }

luis_pereira87's avatar
luis_pereira87 committed
677
  /* Free all NAS PDUs */
678 679
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
    clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
luis_pereira87's avatar
luis_pereira87 committed
680

681
  NR_CellGroupConfig_t *cellGroupConfig = ue_p->masterCellGroup;
682

683
  uint8_t buffer[RRC_BUF_SIZE] = {0};
684
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
685
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
luis_pereira87's avatar
luis_pereira87 committed
686

687
  int size = do_RRCReconfiguration(ue_p,
688 689 690
                                   buffer,
                                   RRC_BUF_SIZE,
                                   xid,
laurent's avatar
laurent committed
691
                                   SRBs,
luis_pereira87's avatar
luis_pereira87 committed
692
                                   DRBs,
693
                                   ue_p->DRB_ReleaseList,
694 695 696 697
                                   NULL,
                                   NULL,
                                   dedicatedNAS_MessageList,
                                   cellGroupConfig);
698
  LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n");
laurent's avatar
laurent committed
699
  freeSRBlist(SRBs);
luis_pereira87's avatar
luis_pereira87 committed
700
  freeDRBlist(DRBs);
701 702 703
  ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, ue_p->DRB_ReleaseList);
  ue_p->DRB_ReleaseList = NULL;

704
  LOG_I(NR_RRC, "UE %d: Generate RRCReconfiguration (bytes %d, xid %d)\n", ue_p->rrc_ue_id, size, xid);
705 706
  LOG_D(NR_RRC,
        "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
707 708 709 710 711 712 713
        ctxt_pP->frame,
        ctxt_pP->module_id,
        size,
        ue_p->rnti,
        rrc_gNB_mui,
        ctxt_pP->module_id,
        DCCH);
714

715
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
716 717
}

718
//-----------------------------------------------------------------------------
Masayuki Harada's avatar
Masayuki Harada committed
719
void
720 721 722 723 724
rrc_gNB_modify_dedicatedRRCReconfiguration(
  const protocol_ctxt_t     *const ctxt_pP,
  rrc_gNB_ue_context_t      *ue_context_pP)
//-----------------------------------------------------------------------------
{
725
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
726
  int qos_flow_index = 0;
727
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
728
  ue_p->xids[xid] = RRC_PDUSESSION_MODIFY;
729

730 731 732
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList =
      CALLOC(1, sizeof(*dedicatedNAS_MessageList));
  NR_DRB_ToAddMod_t *DRB_config = NULL;
733

734
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
735
    // bypass the new and already configured pdu sessions
736 737
    if (ue_p->pduSession[i].status >= PDU_SESSION_STATUS_DONE) {
      ue_p->pduSession[i].xid = xid;
738 739 740
      continue;
    }

741
    if (ue_p->pduSession[i].cause != NGAP_CAUSE_NOTHING) {
742
      // set xid of failure pdu session
743 744
      ue_p->pduSession[i].xid = xid;
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
745 746 747 748
      continue;
    }

    // search exist DRB_config
749
    int j;
750
    for (j = 0; i < MAX_DRBS_PER_UE; j++) {
751 752
      if (ue_p->established_drbs[j].status != DRB_INACTIVE
          && ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == ue_p->pduSession[i].param.pdusession_id)
753 754 755
        break;
    }

756
    if (j == MAX_DRBS_PER_UE) {
757 758 759 760
      ue_p->pduSession[i].xid = xid;
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
      ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
      ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_unspecified;
761 762 763 764
      continue;
    }

    // Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
765 766
    for (qos_flow_index = 0; qos_flow_index < ue_p->pduSession[i].param.nb_qos; qos_flow_index++) {
      switch (ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI) {
767 768 769 770 771 772 773 774 775 776 777 778 779
        case 1: //100ms
        case 2: //150ms
        case 3: //50ms
        case 4: //300ms
        case 5: //100ms
        case 6: //300ms
        case 7: //100ms
        case 8: //300ms
        case 9: //300ms Video (Buffered Streaming)TCP-based (e.g., www, e-mail, chat, ftp, p2p file sharing, progressive video, etc.)
          // TODO
          break;

        default:
780 781 782 783 784
          LOG_E(NR_RRC, "not supported 5qi %lu\n", ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
          ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
          ue_p->pduSession[i].xid = xid;
          ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
          ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_not_supported_5QI_value;
785 786
          continue;
      }
787 788 789 790 791 792
      LOG_I(NR_RRC,
            "PDU SESSION ID %ld, DRB ID %ld (index %d), QOS flow %d, 5QI %ld \n",
            DRB_config->cnAssociation->choice.sdap_Config->pdu_Session,
            DRB_config->drb_Identity,
            i,
            qos_flow_index,
793
            ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
794 795
    }

796 797
    ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
    ue_p->pduSession[i].xid = xid;
798

799 800 801 802
    if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
      asn1cSequenceAdd(dedicatedNAS_MessageList->list,NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
      OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)ue_p->pduSession[i].param.nas_pdu.buffer, ue_p->pduSession[i].param.nas_pdu.length);
      LOG_I(NR_RRC, "add NAS info with size %d (pdusession id %d)\n", ue_p->pduSession[i].param.nas_pdu.length, ue_p->pduSession[i].param.pdusession_id);
803 804 805 806 807 808 809 810 811
    }
  }

  /* If list is empty free the list and reset the address */
  if (dedicatedNAS_MessageList->list.count == 0) {
    free(dedicatedNAS_MessageList);
    dedicatedNAS_MessageList = NULL;
  }

812
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
813
  uint8_t buffer[RRC_BUF_SIZE];
814
  int size = do_RRCReconfiguration(ue_p,
815 816 817 818
                                   buffer,
                                   RRC_BUF_SIZE,
                                   xid,
                                   NULL,
819
                                   DRBs,
820 821 822 823 824
                                   NULL,
                                   NULL,
                                   NULL,
                                   dedicatedNAS_MessageList,
                                   NULL);
825
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
826
  freeDRBlist(DRBs);
827 828

  /* Free all NAS PDUs */
829 830
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
    clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
831

832
  LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate RRCReconfiguration (bytes %d, UE RNTI %x)\n", ctxt_pP->module_id, ctxt_pP->frame, size, ue_p->rnti);
833 834
  LOG_D(NR_RRC,
        "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
835 836 837 838 839 840 841
        ctxt_pP->frame,
        ctxt_pP->module_id,
        size,
        ue_p->rnti,
        rrc_gNB_mui,
        ctxt_pP->module_id,
        DCCH);
842

843
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
844
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
845 846
}

847 848 849 850 851 852 853 854 855 856
//-----------------------------------------------------------------------------
void
rrc_gNB_generate_dedicatedRRCReconfiguration_release(
    const protocol_ctxt_t   *const ctxt_pP,
    rrc_gNB_ue_context_t    *const ue_context_pP,
    uint8_t                  xid,
    uint32_t                 nas_length,
    uint8_t                 *nas_buffer)
//-----------------------------------------------------------------------------
{
857
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
858

859
  NR_DRB_ToReleaseList_t *DRB_Release_configList2 = CALLOC(sizeof(*DRB_Release_configList2), 1);
860

861
  for (int i = 0; i < NB_RB_MAX; i++) {
862
    if ((ue_p->pduSession[i].status == PDU_SESSION_STATUS_TORELEASE) && ue_p->pduSession[i].xid == xid) {
863 864
      asn1cSequenceAdd(DRB_Release_configList2->list, NR_DRB_Identity_t, DRB_release);
      *DRB_release = i + 1;
865 866 867 868
    }
  }

  /* If list is empty free the list and reset the address */
869
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL;
870
  if (nas_length > 0) {
871 872 873
    dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));
    asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
    OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)nas_buffer, nas_length);
874 875 876 877 878
    LOG_I(NR_RRC,"add NAS info with size %d\n", nas_length);
  } else {
    LOG_W(NR_RRC,"dedlicated NAS list is empty\n");
  }

879
  uint8_t buffer[RRC_BUF_SIZE] = {0};
880
  int size = do_RRCReconfiguration(ue_p,
881 882 883 884 885
                                   buffer,
                                   RRC_BUF_SIZE,
                                   xid,
                                   NULL,
                                   NULL,
886
                                   DRB_Release_configList2,
887 888 889 890
                                   NULL,
                                   NULL,
                                   dedicatedNAS_MessageList,
                                   NULL);
891
  LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n");
892 893 894 895 896 897 898

  /* Free all NAS PDUs */
  if (nas_length > 0) {
    /* Free the NAS PDU buffer and invalidate it */
    free(nas_buffer);
  }

899
  LOG_I(NR_RRC, "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate NR_RRCReconfiguration (bytes %d, UE RNTI %x)\n", ctxt_pP->module_id, ctxt_pP->frame, size, ue_p->rnti);
900 901
  LOG_D(NR_RRC,
        "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n",
902 903 904 905 906 907 908
        ctxt_pP->frame,
        ctxt_pP->module_id,
        size,
        ue_p->rnti,
        rrc_gNB_mui,
        ctxt_pP->module_id,
        DCCH);
luis_pereira87's avatar
luis_pereira87 committed
909

910
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
911
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
912 913
}

914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
/* \brief find existing PDU session inside E1AP Bearer Modif message, or
 * point to new one.
 * \param bearer_modif E1AP Bearer Modification Message
 * \param pdu_id PDU session ID
 * \return pointer to existing PDU session, or to new/unused one. */
static pdu_session_to_mod_t *find_or_next_pdu_session(e1ap_bearer_mod_req_t *bearer_modif, int pdu_id)
{
  for (int i = 0; i < bearer_modif->numPDUSessionsMod; ++i) {
    if (bearer_modif->pduSessionMod[i].sessionId == pdu_id)
      return &bearer_modif->pduSessionMod[i];
  }
  /* E1AP Bearer Modification has no PDU session to modify with that ID, create
   * new entry */
  DevAssert(bearer_modif->numPDUSessionsMod < E1AP_MAX_NUM_PDU_SESSIONS - 1);
  bearer_modif->numPDUSessionsMod += 1;
  return &bearer_modif->pduSessionMod[bearer_modif->numPDUSessionsMod - 1];
}

Guido Casati's avatar
Guido Casati committed
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
/**
 * @brief Notify E1 re-establishment to CU-UP
 */
static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
{
  e1ap_bearer_mod_req_t req = {
      .gNB_cu_cp_ue_id = ue_p->rrc_ue_id,
      .gNB_cu_up_ue_id = ue_p->rrc_ue_id,
  };
  if (!ue_associated_to_cuup(rrc, ue_p))
    return;
  /* loop through active DRBs */
  for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
    if (ue_p->established_drbs[drb_id - 1].status == DRB_INACTIVE)
      continue;
    /* fetch an existing PDU session for this DRB */
    rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id);
    if (pdu == NULL) {
      LOG_E(RRC, "UE %d: E1 Bearer Context Modification: no PDU session for DRB ID %d\n", ue_p->rrc_ue_id, drb_id);
      continue;
    }
    /* Get pointer to existing (or new one) PDU session to modify in E1 */
    pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
    AssertError(pdu != NULL,
                continue,
                "UE %d: E1 Bearer Context Modification: PDU session %d to setup is null\n",
                ue_p->rrc_ue_id,
                pdu->param.pdusession_id);
    /* Prepare PDU for E1 Bearear Context Modification Request */
    pdu_e1->sessionId = pdu->param.pdusession_id;
    /* Fill DRB to setup with ID, DL TL and DL TEID */
    DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
    drb_e1->id = drb_id;
    drb_e1->numDlUpParam = 1;
    drb_t *drbs = &ue_p->established_drbs[drb_id];
    memcpy(&drb_e1->DlUpParamList[0].tlAddress, &drbs->f1u_tunnel_config.cuup_addr_f1u.buffer, sizeof(uint8_t) * 4);
    drb_e1->DlUpParamList[0].teId = drbs->f1u_tunnel_config.cuup_teid_f1u;
    /* PDCP configuration */
    bearer_context_pdcp_config_t *pdcp_config = &drb_e1->pdcp_config;
    drb_t *rrc_drb = get_drb(ue_p, drb_id);
    set_bearer_context_pdcp_config(pdcp_config, rrc_drb, rrc->configuration.um_on_default_drb);
    pdcp_config->pDCP_Reestablishment = true;
    /* increase DRB to modify counter */
    pdu_e1->numDRB2Modify += 1;
  }
  /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */
  sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p);
  rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}

/**
 * @brief RRCReestablishment message
 *        Direction: Network to UE
 */
986
static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context_pP,
987
                                                const uint8_t *masterCellGroup_from_DU,
988
                                                const rnti_t old_rnti,
989
                                                const nr_rrc_du_container_t *du)
yaojie's avatar
yaojie committed
990
{
991 992
  module_id_t module_id = 0;
  gNB_RRC_INST *rrc = RC.nrrrc[module_id];
993
  int enable_ciphering = 0;
994
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
995
  uint8_t buffer[RRC_BUF_SIZE] = {0};
996 997
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(module_id);
  ue_p->xids[xid] = RRC_REESTABLISH;
998 999 1000
  const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
  uint32_t ssb_arfcn = get_ssb_arfcn(cell_info, du->mib, du->sib1);
  int size = do_RRCReestablishment(ue_context_pP, buffer, RRC_BUF_SIZE, xid, cell_info->nr_pci, ssb_arfcn);
1001

1002
  LOG_I(NR_RRC, "[RAPROC] UE %04x Logical Channel DL-DCCH, Generating NR_RRCReestablishment (bytes %d)\n", ue_p->rnti, size);
1003

1004 1005 1006 1007
  /* Ciphering and Integrity according to TS 33.501 */
  uint8_t kRRCenc[NR_K_KEY_SIZE] = {0};
  uint8_t kRRCint[NR_K_KEY_SIZE] = {0};
  uint8_t  kUPenc[NR_K_KEY_SIZE] = {0};
1008
  /* Derive the keys from kgnb */
laurent's avatar
laurent committed
1009
  if (ue_p->Srb[1].Active)
mir's avatar
mir committed
1010 1011 1012
    nr_derive_key(UP_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kUPenc);
  nr_derive_key(RRC_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, kRRCenc);
  nr_derive_key(RRC_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, kRRCint);
1013 1014 1015 1016 1017 1018
  LOG_I(NR_RRC,
        "Set PDCP security UE %d RNTI %04x nca %ld nia %d in RRCReestablishment\n",
        ue_p->rrc_ue_id,
        ue_p->rnti,
        ue_p->ciphering_algorithm,
        ue_p->integrity_algorithm);
1019 1020 1021
  uint8_t security_mode =
      enable_ciphering ? ue_p->ciphering_algorithm | (ue_p->integrity_algorithm << 4) : 0 | (ue_p->integrity_algorithm << 4);

1022 1023
  /* SRBs */
  for (int srb_id = 1; srb_id < NR_NUM_SRB; srb_id++) {
1024 1025
    if (ue_p->Srb[srb_id].Active) {
      nr_pdcp_config_set_security(ue_p->rrc_ue_id, srb_id, security_mode, kRRCenc, kRRCint, kUPenc);
1026
      nr_pdcp_reestablishment(ue_p->rrc_ue_id, srb_id, true);
1027 1028
    }
  }
Guido Casati's avatar
Guido Casati committed
1029 1030 1031
  /* PDCP Reestablishment over E1 */
  cuup_notify_reestablishment(rrc, ue_p);
  /* F1AP DL RRC Message Transfer */
1032
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
1033
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
1034 1035 1036 1037 1038
  uint32_t old_gNB_DU_ue_id = old_rnti;
  f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id,
                                  .gNB_DU_ue_id = ue_data.secondary_ue,
                                  .srb_id = DCCH,
                                  .old_gNB_DU_ue_id = &old_gNB_DU_ue_id};
Robert Schmidt's avatar
Robert Schmidt committed
1039
  deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc, .assoc_id = ue_data.du_assoc_id};
1040
  nr_pdcp_data_req_srb(ue_p->rrc_ue_id, DCCH, rrc_gNB_mui++, size, (unsigned char *const)buffer, rrc_deliver_dl_rrc_message, &data);
1041 1042
}

1043 1044 1045 1046 1047
/// @brief Function tha processes RRCReestablishmentComplete message sent by the UE, after RRCReestasblishment request.
/// @param ctxt_pP Protocol context containing information regarding the UE and gNB
/// @param reestablish_rnti is the old C-RNTI
/// @param ue_context_pP  UE context container information regarding the UE
/// @param xid Transaction Identifier used in RRC messages
1048 1049 1050
static void rrc_gNB_process_RRCReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP,
                                                       rrc_gNB_ue_context_t *ue_context_pP,
                                                       const uint8_t xid)
1051
{
1052
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
1053
  LOG_I(NR_RRC, "UE %d Processing NR_RRCReestablishmentComplete from UE\n", ue_p->rrc_ue_id);
1054 1055 1056

  int i = 0;

1057
  ue_p->xids[xid] = RRC_ACTION_NONE;
1058
  ue_p->StatusRrc = NR_RRC_CONNECTED;
laurent's avatar
laurent committed
1059 1060

  ue_p->Srb[1].Active = 1;
1061 1062 1063

  uint8_t send_security_mode_command = false;
  nr_rrc_pdcp_config_security(ctxt_pP, ue_context_pP, send_security_mode_command);
1064

1065 1066
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
  NR_CellGroupConfig_t *cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t));
1067

1068
  cellGroupConfig->spCellConfig = ue_p->masterCellGroup->spCellConfig;
1069
  cellGroupConfig->mac_CellGroupConfig = ue_p->masterCellGroup->mac_CellGroupConfig;
1070
  cellGroupConfig->physicalCellGroupConfig = ue_p->masterCellGroup->physicalCellGroupConfig;
1071 1072 1073 1074 1075 1076 1077 1078 1079
  cellGroupConfig->rlc_BearerToReleaseList = NULL;
  cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList));

  /*
   * Get SRB2, DRB configuration from the existing UE context,
   * also start from SRB2 (i=1) and not from SRB1 (i=0).
   */
  for (i = 1; i < ue_p->masterCellGroup->rlc_BearerToAddModList->list.count; ++i)
    asn1cSeqAdd(&cellGroupConfig->rlc_BearerToAddModList->list, ue_p->masterCellGroup->rlc_BearerToAddModList->list.array[i]);
1080 1081

  for (i = 0; i < cellGroupConfig->rlc_BearerToAddModList->list.count; i++) {
1082 1083
    asn1cCallocOne(cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC,
                   NR_RLC_BearerConfig__reestablishRLC_true);
1084 1085
  }

1086
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, true);
1087 1088
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, true);

1089 1090
  uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
  ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
1091
  uint8_t buffer[RRC_BUF_SIZE] = {0};
1092
  int size = do_RRCReconfiguration(ue_p,
1093 1094 1095
                                   buffer,
                                   RRC_BUF_SIZE,
                                   new_xid,
laurent's avatar
laurent committed
1096
                                   SRBs,
1097
                                   DRBs,
1098 1099 1100 1101 1102
                                   NULL,
                                   NULL,
                                   NULL, // MeasObj_list,
                                   NULL,
                                   cellGroupConfig);
laurent's avatar
laurent committed
1103
  freeSRBlist(SRBs);
1104
  freeDRBlist(DRBs);
1105
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
1106

1107 1108 1109 1110 1111 1112 1113
  AssertFatal(size > 0, "cannot encode RRC Reconfiguration\n");
  LOG_I(NR_RRC,
        "UE %d RNTI %04x: Generate NR_RRCReconfiguration after reestablishment complete (bytes %d)\n",
        ue_p->rrc_ue_id,
        ue_p->rnti,
        size);
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
1114
}
1115 1116
//-----------------------------------------------------------------------------

1117 1118 1119 1120 1121
int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP,
                               protocol_ctxt_t *const ctxt_pP,
                               const int dl_bwp_id,
                               const int ul_bwp_id)
{
1122
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
1123
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
1124
  ue_p->xids[xid] = RRC_DEDICATED_RECONF;
1125

1126
  NR_CellGroupConfig_t *masterCellGroup = ue_p->masterCellGroup;
1127
  if (dl_bwp_id > 0) {
1128 1129 1130
    *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = dl_bwp_id;
    *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = dl_bwp_id;
  }
1131
  if (ul_bwp_id > 0) {
1132 1133
    *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id;
  }
1134

1135
  uint8_t buffer[RRC_BUF_SIZE];
1136
  int size = do_RRCReconfiguration(ue_p, buffer, RRC_BUF_SIZE, xid, NULL, NULL, NULL, NULL, NULL, NULL, masterCellGroup);
1137

1138
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
1139
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
luis_pereira87's avatar
luis_pereira87 committed
1140

1141 1142 1143
  return 0;
}

1144 1145 1146 1147
static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
                                       sctp_assoc_t assoc_id,
                                       const NR_RRCSetupRequest_IEs_t *rrcSetupRequest,
                                       const f1ap_initial_ul_rrc_message_t *msg)
1148
{
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
  rrc_gNB_ue_context_t *ue_context_p = NULL;
  if (NR_InitialUE_Identity_PR_randomValue == rrcSetupRequest->ue_Identity.present) {
    /* randomValue                         BIT STRING (SIZE (39)) */
    if (rrcSetupRequest->ue_Identity.choice.randomValue.size != 5) { // 39-bit random value
      LOG_E(NR_RRC,
            "wrong InitialUE-Identity randomValue size, expected 5, provided %lu",
            (long unsigned int)rrcSetupRequest->ue_Identity.choice.randomValue.size);
      return;
    }
    uint64_t random_value = 0;
    memcpy(((uint8_t *)&random_value) + 3,
           rrcSetupRequest->ue_Identity.choice.randomValue.buf,
           rrcSetupRequest->ue_Identity.choice.randomValue.size);

Robert Schmidt's avatar
Robert Schmidt committed
1163
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1164 1165 1166 1167 1168 1169 1170 1171 1172
  } else if (NR_InitialUE_Identity_PR_ng_5G_S_TMSI_Part1 == rrcSetupRequest->ue_Identity.present) {
    /* <5G-S-TMSI> = <AMF Set ID><AMF Pointer><5G-TMSI> 48-bit */
    /* ng-5G-S-TMSI-Part1                  BIT STRING (SIZE (39)) */
    if (rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size != 5) {
      LOG_E(NR_RRC,
            "wrong ng_5G_S_TMSI_Part1 size, expected 5, provided %lu \n",
            (long unsigned int)rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size);
      return;
    }
1173

1174
    uint64_t s_tmsi_part1 = BIT_STRING_to_uint64(&rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1);
1175
    LOG_I(NR_RRC, "Received UE 5G-S-TMSI-Part1 %ld\n", s_tmsi_part1);
1176

1177 1178 1179 1180 1181
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, s_tmsi_part1, msg->gNB_DU_ue_id);
    AssertFatal(ue_context_p != NULL, "out of memory\n");
    gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
    UE->Initialue_identity_5g_s_TMSI.presence = true;
    UE->ng_5G_S_TMSI_Part1 = s_tmsi_part1;
1182 1183 1184 1185 1186 1187
  } else {
    uint64_t random_value = 0;
    memcpy(((uint8_t *)&random_value) + 3,
           rrcSetupRequest->ue_Identity.choice.randomValue.buf,
           rrcSetupRequest->ue_Identity.choice.randomValue.size);

Robert Schmidt's avatar
Robert Schmidt committed
1188
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1189 1190 1191 1192
    LOG_E(NR_RRC, "RRCSetupRequest without random UE identity or S-TMSI not supported, let's reject the UE %04x\n", msg->crnti);
    rrc_gNB_generate_RRCReject(0, ue_context_p);
    return;
  }
yaojie's avatar
yaojie committed
1193

1194 1195 1196
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  UE = &ue_context_p->ue_context;
  UE->establishment_cause = rrcSetupRequest->establishmentCause;
1197
  activate_srb(UE, 1);
1198 1199
  rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
}
1200

1201
static const char *get_reestab_cause(NR_ReestablishmentCause_t c)
1202
{
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
  switch (c) {
    case NR_ReestablishmentCause_otherFailure:
      return "Other Failure";
    case NR_ReestablishmentCause_handoverFailure:
      return "Handover Failure";
    case NR_ReestablishmentCause_reconfigurationFailure:
      return "Reconfiguration Failure";
    default:
      break;
  }
  return "UNKNOWN Failure (ASN.1 decoder error?)";
}
1215

1216 1217 1218 1219
static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
                                                 sctp_assoc_t assoc_id,
                                                 const NR_RRCReestablishmentRequest_IEs_t *req,
                                                 const f1ap_initial_ul_rrc_message_t *msg)
1220 1221 1222
{
  uint64_t random_value = 0;
  const char *scause = get_reestab_cause(req->reestablishmentCause);
1223
  const long physCellId = req->ue_Identity.physCellId;
1224
  LOG_D(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause);
1225

1226 1227 1228 1229 1230
  const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id);
  if (du == NULL) {
    LOG_E(RRC, "received CCCH message, but no corresponding DU found\n");
    return;
  }
1231

1232 1233 1234 1235 1236
  if (du->mib == NULL || du->sib1 == NULL) {
    /* we don't have MIB/SIB1 of the DU, and therefore cannot generate the
     * Reestablishment (as we would need the SSB's ARFCN, which we cannot
     * compute). So generate RRC Setup instead */
    LOG_E(NR_RRC, "Reestablishment request: no MIB/SIB1 of DU present, cannot do reestablishment, force setup request\n");
1237
    goto fallback_rrc_setup;
1238 1239 1240 1241
  }

  rnti_t old_rnti = req->ue_Identity.c_RNTI;
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti);
1242 1243
  if (ue_context_p == NULL) {
    LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
1244
    goto fallback_rrc_setup;
1245
  }
1246

1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
  const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
  if (physCellId != cell_info->nr_pci) {
    /* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */
    LOG_I(NR_RRC,
          "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n",
          physCellId,
          cell_info->nr_pci);
    /* 38.401 8.7: "If the UE accessed from a gNB-DU other than the original
     * one, the gNB-CU should trigger the UE Context Setup procedure". Let's
     * assume that the old DU will trigger a release request, also freeing the
     * ongoing context at the CU. Hence, create new below */
1258
    goto fallback_rrc_setup;
1259 1260
  }

1261
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1262 1263 1264
  // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values
  if (req->ue_Identity.c_RNTI < 0x1 || req->ue_Identity.c_RNTI > 0xffef) {
    /* c_RNTI range error should not happen */
1265
    LOG_E(NR_RRC, "NR_RRCReestablishmentRequest c_RNTI %04lx range error, fallback to RRC setup\n", req->ue_Identity.c_RNTI);
1266
    goto fallback_rrc_setup;
1267
  }
1268

1269 1270 1271
  if (!UE->as_security_active) {
    /* no active security context, need to restart entire connection */
    LOG_E(NR_RRC, "UE requested Reestablishment without activated AS security\n");
1272
    goto fallback_rrc_setup;
1273 1274
  }

1275
  /* TODO: start timer in ITTI and drop UE if it does not come back */
1276

1277 1278
  // update with new RNTI, and update secondary UE association
  UE->rnti = msg->crnti;
1279 1280
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
  ue_data.secondary_ue = msg->gNB_DU_ue_id;
1281 1282
  cu_remove_f1_ue_data(UE->rrc_ue_id);
  cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
1283

1284
  LOG_I(NR_RRC, "Accept Reestablishment Request UE physCellId %ld cause %s\n", physCellId, scause);
1285

1286
  rrc_gNB_generate_RRCReestablishment(ue_context_p, msg->du2cu_rrc_container, old_rnti, du);
1287 1288 1289 1290 1291 1292 1293
  return;

fallback_rrc_setup:
  fill_random(&random_value, sizeof(random_value));
  random_value = random_value & 0x7fffffffff; /* random value is 39 bits */

  rrc_gNB_ue_context_t *new = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1294
  activate_srb(&new->ue_context, 1);
1295 1296
  rrc_gNB_generate_RRCSetup(0, msg->crnti, new, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
  return;
1297 1298
}

Robert Schmidt's avatar
Robert Schmidt committed
1299
static void rrc_gNB_process_MeasurementReport(rrc_gNB_ue_context_t *ue_context, NR_MeasurementReport_t *measurementReport)
1300 1301 1302 1303 1304 1305 1306
{
  if (LOG_DEBUGFLAG(DEBUG_ASN1))
    xer_fprint(stdout, &asn_DEF_NR_MeasurementReport, (void *)measurementReport);

  DevAssert(measurementReport->criticalExtensions.present == NR_MeasurementReport__criticalExtensions_PR_measurementReport
            && measurementReport->criticalExtensions.choice.measurementReport != NULL);

1307
  gNB_RRC_UE_t *ue_ctxt = &ue_context->ue_context;
Robert Schmidt's avatar
Robert Schmidt committed
1308 1309
  ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, ue_ctxt->measResults);
  ue_ctxt->measResults = NULL;
1310

1311 1312 1313
  const NR_MeasId_t id = measurementReport->criticalExtensions.choice.measurementReport->measResults.measId;
  AssertFatal(id, "unexpected MeasResult for MeasurementId %ld received\n", id);
  asn1cCallocOne(ue_ctxt->measResults, measurementReport->criticalExtensions.choice.measurementReport->measResults);
Robert Schmidt's avatar
Robert Schmidt committed
1314 1315 1316
  /* we "keep" the measurement report, so set to 0 */
  free(measurementReport->criticalExtensions.choice.measurementReport);
  measurementReport->criticalExtensions.choice.measurementReport = NULL;
1317 1318
}

1319
static int handle_rrcReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP,
1320
                                             rrc_gNB_ue_context_t *ue_context_p,
1321 1322
                                             const NR_RRCReestablishmentComplete_t *reestablishment_complete)
{
1323 1324
  DevAssert(ue_context_p != NULL);
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1325 1326 1327

  DevAssert(reestablishment_complete->criticalExtensions.present
            == NR_RRCReestablishmentComplete__criticalExtensions_PR_rrcReestablishmentComplete);
1328
  rrc_gNB_process_RRCReestablishmentComplete(ctxt_pP, ue_context_p, reestablishment_complete->rrc_TransactionIdentifier);
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340

  UE->ue_reestablishment_counter++;
  return 0;
}

static int handle_ueCapabilityInformation(const protocol_ctxt_t *const ctxt_pP,
                                          rrc_gNB_ue_context_t *ue_context_p,
                                          const NR_UECapabilityInformation_t *ue_cap_info)
{
  AssertFatal(ue_context_p != NULL, "Processing %s() for UE %lx, ue_context_p is NULL\n", __func__, ctxt_pP->rntiMaybeUEid);
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;

1341 1342 1343 1344
  int xid = ue_cap_info->rrc_TransactionIdentifier;
  DevAssert(UE->xids[xid] == RRC_UECAPABILITY_ENQUIRY);
  UE->xids[xid] = RRC_ACTION_NONE;

1345
  LOG_I(NR_RRC, "UE %d: received UE capabilities (xid %d)\n", UE->rrc_ue_id, xid);
1346 1347 1348 1349 1350
  int eutra_index = -1;

  if (ue_cap_info->criticalExtensions.present == NR_UECapabilityInformation__criticalExtensions_PR_ueCapabilityInformation) {
    const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList =
        ue_cap_info->criticalExtensions.choice.ueCapabilityInformation->ue_CapabilityRAT_ContainerList;
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362

    /* Encode UE-CapabilityRAT-ContainerList for sending to the DU */
    free(UE->ue_cap_buffer.buf);
    UE->ue_cap_buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
                                                      NULL,
                                                      ue_CapabilityRAT_ContainerList,
                                                      (void **)&UE->ue_cap_buffer.buf);
    if (UE->ue_cap_buffer.len <= 0) {
      LOG_E(RRC, "could not encode UE-CapabilityRAT-ContainerList, abort handling capabilities\n");
      return -1;
    }

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
    for (int i = 0; i < ue_CapabilityRAT_ContainerList->list.count; i++) {
      const NR_UE_CapabilityRAT_Container_t *ue_cap_container = ue_CapabilityRAT_ContainerList->list.array[i];
      if (ue_cap_container->rat_Type == NR_RAT_Type_nr) {
        if (UE->UE_Capability_nr) {
          ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
          UE->UE_Capability_nr = 0;
        }

        asn_dec_rval_t dec_rval = uper_decode(NULL,
                                              &asn_DEF_NR_UE_NR_Capability,
                                              (void **)&UE->UE_Capability_nr,
                                              ue_cap_container->ue_CapabilityRAT_Container.buf,
                                              ue_cap_container->ue_CapabilityRAT_Container.size,
                                              0,
                                              0);
        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
          xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
        }

        if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
          LOG_E(NR_RRC,
                PROTOCOL_NR_RRC_CTXT_UE_FMT " Failed to decode nr UE capabilities (%zu bytes)\n",
                PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
                dec_rval.consumed);
          ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
          UE->UE_Capability_nr = 0;
        }

        UE->UE_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
        if (eutra_index != -1) {
          LOG_E(NR_RRC, "fatal: more than 1 eutra capability\n");
          exit(1);
        }
        eutra_index = i;
      }

      if (ue_cap_container->rat_Type == NR_RAT_Type_eutra_nr) {
        if (UE->UE_Capability_MRDC) {
          ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
          UE->UE_Capability_MRDC = 0;
        }
        asn_dec_rval_t dec_rval = uper_decode(NULL,
                                              &asn_DEF_NR_UE_MRDC_Capability,
                                              (void **)&UE->UE_Capability_MRDC,
                                              ue_cap_container->ue_CapabilityRAT_Container.buf,
                                              ue_cap_container->ue_CapabilityRAT_Container.size,
                                              0,
                                              0);

        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
          xer_fprint(stdout, &asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
        }

        if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
          LOG_E(NR_RRC,
                PROTOCOL_NR_RRC_CTXT_FMT " Failed to decode nr UE capabilities (%zu bytes)\n",
                PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
                dec_rval.consumed);
          ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
          UE->UE_Capability_MRDC = 0;
        }
        UE->UE_MRDC_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
      }

      if (ue_cap_container->rat_Type == NR_RAT_Type_eutra) {
        // TODO
      }
    }

    if (eutra_index == -1)
      return -1;
  }

1436
  rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(ctxt_pP, ue_context_p, ue_cap_info);
1437

1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
  /* if we already have DRBs setup (SRB2 exists if there is at least one DRB),
   * there might not be a further reconfiguration on which we can rely, so send
   * the UE capabilities to the DU to have it trigger a reconfiguration. */
  if (UE->Srb[2].Active && UE->ue_cap_buffer.len > 0 && UE->ue_cap_buffer.buf != NULL) {
    cu_to_du_rrc_information_t cu2du = {0};
    if (UE->ue_cap_buffer.len > 0 && UE->ue_cap_buffer.buf != NULL) {
      cu2du.uE_CapabilityRAT_ContainerList = UE->ue_cap_buffer.buf;
      cu2du.uE_CapabilityRAT_ContainerList_length = UE->ue_cap_buffer.len;
    }

    f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
    if (ue_data.du_assoc_id == 0) {
      LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n");
      return -1;
    }
    gNB_RRC_INST *rrc = RC.nrrrc[0];
    f1ap_ue_context_modif_req_t ue_context_modif_req = {
        .gNB_CU_ue_id = UE->rrc_ue_id,
        .gNB_DU_ue_id = ue_data.secondary_ue,
        .plmn.mcc = rrc->configuration.mcc[0],
        .plmn.mnc = rrc->configuration.mnc[0],
        .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
        .nr_cellid = rrc->nr_cellid,
        .servCellId = 0, /* TODO: correct value? */
        .cu_to_du_rrc_information = &cu2du,
    };
    rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &ue_context_modif_req);
  }
1466

1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
  return 0;
}

static int handle_rrcSetupComplete(const protocol_ctxt_t *const ctxt_pP,
                                   rrc_gNB_ue_context_t *ue_context_p,
                                   const NR_RRCSetupComplete_t *setup_complete)
{
  if (!ue_context_p) {
    LOG_I(NR_RRC, "Processing NR_RRCSetupComplete UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
    return -1;
  }
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1479 1480
  uint8_t xid = setup_complete->rrc_TransactionIdentifier;
  UE->xids[xid] = RRC_ACTION_NONE;
1481 1482 1483 1484

  NR_RRCSetupComplete_IEs_t *setup_complete_ies = setup_complete->criticalExtensions.choice.rrcSetupComplete;

  if (setup_complete_ies->ng_5G_S_TMSI_Value != NULL) {
1485
    uint64_t fiveg_s_TMSI = 0;
1486
    if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI_Part2) {
1487 1488 1489
      const BIT_STRING_t *part2 = &setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI_Part2;
      if (part2->size != 2) {
        LOG_E(NR_RRC, "wrong ng_5G_S_TMSI_Part2 size, expected 2, provided %lu", part2->size);
1490 1491 1492
        return -1;
      }

1493 1494 1495 1496 1497 1498 1499 1500
      if (UE->Initialue_identity_5g_s_TMSI.presence) {
        uint16_t stmsi_part2 = BIT_STRING_to_uint16(part2);
        LOG_I(RRC, "s_tmsi part2 %d (%02x %02x)\n", stmsi_part2, part2->buf[0], part2->buf[1]);
        // Part2 is leftmost 9, Part1 is rightmost 39 bits of 5G-S-TMSI
        fiveg_s_TMSI = ((uint64_t) stmsi_part2) << 39 | UE->ng_5G_S_TMSI_Part1;
      } else {
        LOG_W(RRC, "UE %d received 5G-S-TMSI-Part2, but no 5G-S-TMSI-Part1 present, won't send 5G-S-TMSI to core\n", UE->rrc_ue_id);
        UE->Initialue_identity_5g_s_TMSI.presence = false;
1501 1502
      }
    } else if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI) {
1503 1504 1505
      const NR_NG_5G_S_TMSI_t *bs_stmsi = &setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI;
      if (bs_stmsi->size != 6) {
        LOG_E(NR_RRC, "wrong ng_5G_S_TMSI size, expected 6, provided %lu", bs_stmsi->size);
1506 1507 1508
        return -1;
      }

1509 1510 1511 1512 1513 1514 1515 1516
      fiveg_s_TMSI = BIT_STRING_to_uint64(bs_stmsi);
      UE->Initialue_identity_5g_s_TMSI.presence = true;
    }

    if (UE->Initialue_identity_5g_s_TMSI.presence) {
      uint16_t amf_set_id = fiveg_s_TMSI >> 38;
      uint8_t amf_pointer = (fiveg_s_TMSI >> 32) & 0x3F;
      uint32_t fiveg_tmsi = (uint32_t) fiveg_s_TMSI;
1517
      LOG_I(NR_RRC,
1518
            "5g_s_TMSI: 0x%lX, amf_set_id: 0x%X (%d), amf_pointer: 0x%X (%d), 5g TMSI: 0x%X \n",
1519
            fiveg_s_TMSI,
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530
            amf_set_id,
            amf_set_id,
            amf_pointer,
            amf_pointer,
            fiveg_tmsi);
      UE->Initialue_identity_5g_s_TMSI.amf_set_id = amf_set_id;
      UE->Initialue_identity_5g_s_TMSI.amf_pointer = amf_pointer;
      UE->Initialue_identity_5g_s_TMSI.fiveg_tmsi = fiveg_tmsi;

      // update random identity with 5G-S-TMSI, which only contained Part1 of it
      UE->random_ue_identity = fiveg_s_TMSI;
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546
    }
  }

  rrc_gNB_process_RRCSetupComplete(ctxt_pP, ue_context_p, setup_complete->criticalExtensions.choice.rrcSetupComplete);
  LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT " UE State = NR_RRC_CONNECTED \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
  return 0;
}

static void handle_rrcReconfigurationComplete(const protocol_ctxt_t *const ctxt_pP,
                                              rrc_gNB_ue_context_t *ue_context_p,
                                              const NR_RRCReconfigurationComplete_t *reconfig_complete)
{
  AssertFatal(ue_context_p != NULL, "Processing %s() for UE %lx, ue_context_p is NULL\n", __func__, ctxt_pP->rntiMaybeUEid);
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;

  uint8_t xid = reconfig_complete->rrc_TransactionIdentifier;
1547
  UE->ue_reconfiguration_counter++;
1548
  LOG_I(NR_RRC, "UE %d: Receive RRC Reconfiguration Complete message (xid %d)\n", UE->rrc_ue_id, xid);
1549

1550
  bool successful_reconfig = true;
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
  switch (UE->xids[xid]) {
    case RRC_PDUSESSION_RELEASE: {
      gtpv1u_gnb_delete_tunnel_req_t req = {0};
      gtpv1u_delete_ngu_tunnel(ctxt_pP->instance, &req);
      // NGAP_PDUSESSION_RELEASE_RESPONSE
      rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(ctxt_pP, ue_context_p, xid);
    } break;
    case RRC_PDUSESSION_ESTABLISH:
      if (UE->nb_of_pdusessions > 0)
        rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(ctxt_pP, ue_context_p, xid);
      break;
    case RRC_PDUSESSION_MODIFY:
      rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(ctxt_pP, ue_context_p, xid);
      break;
    case RRC_DEFAULT_RECONF:
      rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(ctxt_pP, ue_context_p);
      break;
    case RRC_REESTABLISH_COMPLETE:
    case RRC_DEDICATED_RECONF:
      /* do nothing */
      break;
1572 1573 1574 1575
    case RRC_ACTION_NONE:
      LOG_E(RRC, "UE %d: Received RRC Reconfiguration Complete with xid %d while no transaction is ongoing\n", UE->rrc_ue_id, xid);
      successful_reconfig = false;
      break;
1576
    default:
1577
      LOG_E(RRC, "UE %d: Received unexpected transaction type %d for xid %d\n", UE->rrc_ue_id, UE->xids[xid], xid);
1578 1579
      successful_reconfig = false;
      break;
1580
  }
1581
  UE->xids[xid] = RRC_ACTION_NONE;
1582 1583 1584 1585 1586
  for (int i = 0; i < 3; ++i) {
    if (UE->xids[i] != RRC_ACTION_NONE) {
      LOG_I(RRC, "UE %d: transaction %d still ongoing for action %d\n", UE->rrc_ue_id, i, UE->xids[i]);
    }
  }
1587 1588

  gNB_RRC_INST *rrc = RC.nrrrc[0];
1589
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
1590
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
1591
  f1ap_ue_context_modif_req_t ue_context_modif_req = {
1592
    .gNB_CU_ue_id = UE->rrc_ue_id,
1593
    .gNB_DU_ue_id = ue_data.secondary_ue,
1594 1595 1596
    .plmn.mcc = rrc->configuration.mcc[0],
    .plmn.mnc = rrc->configuration.mnc[0],
    .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
1597 1598 1599 1600
    .nr_cellid = rrc->nr_cellid,
    .servCellId = 0, /* TODO: correct value? */
    .ReconfigComplOutcome = successful_reconfig ? RRCreconf_success : RRCreconf_failure,
  };
Robert Schmidt's avatar
Robert Schmidt committed
1601
  rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &ue_context_modif_req);
1602 1603 1604 1605 1606 1607 1608 1609

  /* if we did not receive any UE Capabilities, let's do that now. It should
   * only happen on the first time a reconfiguration arrives. Afterwards, we
   * should have them. If the UE does not give us anything, we will re-request
   * the capabilities after each reconfiguration, which is a finite number */
  if (UE->ue_cap_buffer.len == 0) {
    rrc_gNB_generate_UECapabilityEnquiry(ctxt_pP, ue_context_p);
  }
1610
}
Xue Song's avatar
Xue Song committed
1611
//-----------------------------------------------------------------------------
1612 1613 1614 1615
int rrc_gNB_decode_dcch(const protocol_ctxt_t *const ctxt_pP,
                        const rb_id_t Srb_id,
                        const uint8_t *const Rx_sdu,
                        const sdu_size_t sdu_sizeP)
Xue Song's avatar
Xue Song committed
1616 1617
//-----------------------------------------------------------------------------
{
1618
  gNB_RRC_INST *gnb_rrc_inst = RC.nrrrc[ctxt_pP->module_id];
Xue Song's avatar
Xue Song committed
1619

1620 1621 1622 1623 1624 1625 1626
  /* we look up by CU UE ID! Do NOT change back to RNTI! */
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(gnb_rrc_inst, ctxt_pP->rntiMaybeUEid);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %lu, aborting transaction\n", ctxt_pP->rntiMaybeUEid);
    return -1;
  }

1627
  if ((Srb_id != 1) && (Srb_id != 2)) {
1628
    LOG_E(NR_RRC, "Received message on SRB%ld, should not have ...\n", Srb_id);
1629
  } else {
1630
    LOG_D(NR_RRC, "Received message on SRB%ld\n", Srb_id);
1631 1632
  }

1633
  LOG_D(NR_RRC, "Decoding UL-DCCH Message\n");
1634
  {
1635
    for (int i = 0; i < sdu_sizeP; i++) {
1636
      LOG_T(NR_RRC, "%x.", Rx_sdu[i]);
Xue Song's avatar
Xue Song committed
1637 1638
    }

1639 1640
    LOG_T(NR_RRC, "\n");
  }
Xue Song's avatar
Xue Song committed
1641

1642 1643 1644
  NR_UL_DCCH_Message_t *ul_dcch_msg = NULL;
  asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_UL_DCCH_Message, (void **)&ul_dcch_msg, Rx_sdu, sdu_sizeP, 0, 0);

1645
  if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
1646
    LOG_E(NR_RRC, "Failed to decode UL-DCCH (%zu bytes)\n", dec_rval.consumed);
1647 1648 1649
    return -1;
  }

1650 1651 1652 1653
  if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
    xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
  }

1654 1655 1656
  if (ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1) {
    switch (ul_dcch_msg->message.choice.c1->present) {
      case NR_UL_DCCH_MessageType__c1_PR_NOTHING:
1657
        LOG_I(NR_RRC, "Received PR_NOTHING on UL-DCCH-Message\n");
1658
        break;
Xue Song's avatar
Xue Song committed
1659

1660
      case NR_UL_DCCH_MessageType__c1_PR_rrcReconfigurationComplete:
1661
        handle_rrcReconfigurationComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcReconfigurationComplete);
1662 1663 1664
        break;

      case NR_UL_DCCH_MessageType__c1_PR_rrcSetupComplete:
1665 1666
        if (handle_rrcSetupComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcSetupComplete) == -1)
          return -1;
1667
        break;
wujing's avatar
wujing committed
1668

1669 1670 1671 1672 1673 1674 1675 1676
      case NR_UL_DCCH_MessageType__c1_PR_measurementReport:
        DevAssert(ul_dcch_msg != NULL
                  && ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1
                  && ul_dcch_msg->message.choice.c1
                  && ul_dcch_msg->message.choice.c1->present == NR_UL_DCCH_MessageType__c1_PR_measurementReport);
        rrc_gNB_process_MeasurementReport(ue_context_p, ul_dcch_msg->message.choice.c1->choice.measurementReport);
        break;

1677
      case NR_UL_DCCH_MessageType__c1_PR_ulInformationTransfer:
Robert Schmidt's avatar
Robert Schmidt committed
1678
        LOG_D(NR_RRC, "Recived RRC GNB UL Information Transfer \n");
1679
        if (!ue_context_p) {
Robert Schmidt's avatar
Robert Schmidt committed
1680
          LOG_W(NR_RRC, "Processing ulInformationTransfer UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
1681 1682
          break;
        }
wujing's avatar
wujing committed
1683

1684 1685
        LOG_D(NR_RRC, "[MSG] RRC UL Information Transfer \n");
        LOG_DUMPMSG(RRC, DEBUG_RRC, (char *)Rx_sdu, sdu_sizeP, "[MSG] RRC UL Information Transfer \n");
wujing's avatar
wujing committed
1686

1687
        rrc_gNB_send_NGAP_UPLINK_NAS(ctxt_pP, ue_context_p, ul_dcch_msg);
1688
        break;
wujing's avatar
wujing committed
1689

1690
      case NR_UL_DCCH_MessageType__c1_PR_securityModeComplete:
1691
        // to avoid segmentation fault
1692 1693 1694 1695
        if (!ue_context_p) {
          LOG_I(NR_RRC, "Processing securityModeComplete UE %lx, ue_context_p is NULL\n", ctxt_pP->rntiMaybeUEid);
          break;
        }
1696 1697

        LOG_I(NR_RRC,
1698
              PROTOCOL_NR_RRC_CTXT_UE_FMT " received securityModeComplete on UL-DCCH %d from UE\n",
1699 1700 1701
              PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
              DCCH);
        LOG_D(NR_RRC,
1702 1703
              PROTOCOL_NR_RRC_CTXT_UE_FMT
              " RLC RB %02d --- RLC_DATA_IND %d bytes "
1704 1705 1706 1707 1708
              "(securityModeComplete) ---> RRC_eNB\n",
              PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
              DCCH,
              sdu_sizeP);

1709
        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
1710 1711 1712
          xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
        }

Cedric Roux's avatar
Cedric Roux committed
1713 1714
        /* configure ciphering */
        nr_rrc_pdcp_config_security(ctxt_pP, ue_context_p, 1);
1715
        ue_context_p->ue_context.as_security_active = true;
Cedric Roux's avatar
Cedric Roux committed
1716

1717
        rrc_gNB_generate_defaultRRCReconfiguration(ctxt_pP, ue_context_p);
1718 1719
        break;

1720 1721
      case NR_UL_DCCH_MessageType__c1_PR_securityModeFailure:
        LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)Rx_sdu, sdu_sizeP, "[MSG] NR RRC Security Mode Failure\n");
1722
        LOG_E(NR_RRC, "UE %d: received securityModeFailure\n", ue_context_p->ue_context.rrc_ue_id);
1723

1724 1725
        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
          xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
1726 1727
        }

1728
        LOG_W(NR_RRC, "Cannot continue as no AS security is activated (implementation missing)\n");
1729
        break;
1730

1731 1732 1733 1734 1735
      case NR_UL_DCCH_MessageType__c1_PR_ueCapabilityInformation:
        if (handle_ueCapabilityInformation(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.ueCapabilityInformation)
            == -1)
          return -1;
        break;
1736

1737
      case NR_UL_DCCH_MessageType__c1_PR_rrcReestablishmentComplete:
1738
        if (handle_rrcReestablishmentComplete(ctxt_pP, ue_context_p, ul_dcch_msg->message.choice.c1->choice.rrcReestablishmentComplete)
1739 1740 1741
            == -1)
          return -1;
        break;
1742

1743 1744
      default:
        break;
Xue Song's avatar
Xue Song committed
1745
    }
1746
  }
1747
  ASN_STRUCT_FREE(asn_DEF_NR_UL_DCCH_Message, ul_dcch_msg);
1748
  return 0;
Xue Song's avatar
Xue Song committed
1749 1750
}

Robert Schmidt's avatar
Robert Schmidt committed
1751
void rrc_gNB_process_initial_ul_rrc_message(sctp_assoc_t assoc_id, const f1ap_initial_ul_rrc_message_t *ul_rrc)
1752
{
Robert Schmidt's avatar
Robert Schmidt committed
1753
  AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
1754

1755
  gNB_RRC_INST *rrc = RC.nrrrc[0];
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
  LOG_I(NR_RRC, "Decoding CCCH: RNTI %04x, payload_size %d\n", ul_rrc->crnti, ul_rrc->rrc_container_length);
  NR_UL_CCCH_Message_t *ul_ccch_msg = NULL;
  asn_dec_rval_t dec_rval = uper_decode(NULL,
                                        &asn_DEF_NR_UL_CCCH_Message,
                                        (void **)&ul_ccch_msg,
                                        ul_rrc->rrc_container,
                                        ul_rrc->rrc_container_length,
                                        0,
                                        0);
  if (dec_rval.code != RC_OK || dec_rval.consumed == 0) {
    LOG_E(NR_RRC, " FATAL Error in receiving CCCH\n");
1767 1768
    return;
  }
1769

1770 1771 1772 1773 1774
  if (ul_ccch_msg->message.present == NR_UL_CCCH_MessageType_PR_c1) {
    switch (ul_ccch_msg->message.choice.c1->present) {
      case NR_UL_CCCH_MessageType__c1_PR_NOTHING:
        LOG_W(NR_RRC, "Received PR_NOTHING on UL-CCCH-Message, ignoring message\n");
        break;
Robert Schmidt's avatar
Robert Schmidt committed
1775

1776 1777
      case NR_UL_CCCH_MessageType__c1_PR_rrcSetupRequest:
        LOG_D(NR_RRC, "Received RRCSetupRequest on UL-CCCH-Message (UE rnti %04x)\n", ul_rrc->crnti);
Robert Schmidt's avatar
Robert Schmidt committed
1778
        rrc_handle_RRCSetupRequest(rrc, assoc_id, &ul_ccch_msg->message.choice.c1->choice.rrcSetupRequest->rrcSetupRequest, ul_rrc);
1779
        break;
1780

1781 1782 1783
      case NR_UL_CCCH_MessageType__c1_PR_rrcResumeRequest:
        LOG_E(NR_RRC, "Received rrcResumeRequest message, but handling is not implemented\n");
        break;
Xue Song's avatar
Xue Song committed
1784

1785 1786 1787 1788
      case NR_UL_CCCH_MessageType__c1_PR_rrcReestablishmentRequest: {
        LOG_D(NR_RRC, "Received RRCReestablishmentRequest on UL-CCCH-Message (UE RNTI %04x)\n", ul_rrc->crnti);
        rrc_handle_RRCReestablishmentRequest(
            rrc,
Robert Schmidt's avatar
Robert Schmidt committed
1789
            assoc_id,
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
            &ul_ccch_msg->message.choice.c1->choice.rrcReestablishmentRequest->rrcReestablishmentRequest,
            ul_rrc);
      } break;

      case NR_UL_CCCH_MessageType__c1_PR_rrcSystemInfoRequest:
        LOG_I(NR_RRC, "UE %04x receive rrcSystemInfoRequest message \n", ul_rrc->crnti);
        /* TODO */
        break;

      default:
        LOG_E(NR_RRC, "UE %04x Unknown message\n", ul_rrc->crnti);
        break;
    }
1803
  }
1804
  ASN_STRUCT_FREE(asn_DEF_NR_UL_CCCH_Message, ul_ccch_msg);
1805 1806 1807 1808 1809 1810 1811

  if (ul_rrc->rrc_container)
    free(ul_rrc->rrc_container);
  if (ul_rrc->du2cu_rrc_container)
    free(ul_rrc->du2cu_rrc_container);
}

Cedric Roux's avatar
Cedric Roux committed
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
void rrc_gNB_process_release_request(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_release_request_t *m)
{
  gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
  rrc_remove_nsa_user(rrc, m->rnti);
}

void rrc_gNB_process_dc_overall_timeout(const module_id_t gnb_mod_idP, x2ap_ENDC_dc_overall_timeout_t *m)
{
  gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
  rrc_remove_nsa_user(rrc, m->rnti);
}

1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
/* \brief fill E1 bearer modification's DRB from F1 DRB
 * \param drb_e1 pointer to a DRB inside an E1 bearer modification message
 * \param drb_f1 pointer to a DRB inside an F1 UE Ctxt modification Response */
static void fill_e1_bearer_modif(DRB_nGRAN_to_setup_t *drb_e1, const f1ap_drb_to_be_setup_t *drb_f1)
{
  drb_e1->id = drb_f1->drb_id;
  drb_e1->numDlUpParam = drb_f1->up_dl_tnl_length;
  drb_e1->DlUpParamList[0].tlAddress = drb_f1->up_dl_tnl[0].tl_address;
  drb_e1->DlUpParamList[0].teId = drb_f1->up_dl_tnl[0].teid;
}

1835 1836 1837 1838
/**
 * @brief Store F1-U DL TL and TEID in RRC
*/
static void f1u_gtp_update(f1u_tunnel_t *f1u, const f1ap_drb_to_be_setup_t *drb_f1)
1839
{
1840 1841 1842
  f1u->cuup_teid_f1u = drb_f1->up_dl_tnl[0].teid;
  memcpy(&f1u->cuup_addr_f1u.buffer, &drb_f1->up_dl_tnl[0].tl_address, sizeof(uint8_t) * 4);
  f1u->cuup_addr_f1u.length = sizeof(in_addr_t);
1843 1844 1845 1846 1847 1848 1849
}

/* \brief use list of DRBs and send the corresponding bearer update message via
 * E1 to the CU of this UE */
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_to_be_setup_t *drbs)
{
  // we assume the same UE ID in CU-UP and CU-CP
1850
  e1ap_bearer_mod_req_t req = {
1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865
    .gNB_cu_cp_ue_id = UE->rrc_ue_id,
    .gNB_cu_up_ue_id = UE->rrc_ue_id,
  };

  for (int i = 0; i < n; i++) {
    const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i];
    rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->drb_id);
    if (pdu_ue == NULL) {
      LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %ld\n", UE->rrc_ue_id, drb_f1->drb_id);
      continue;
    }
    pdu_session_to_setup_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
    DevAssert(pdu_e1 != NULL);
    pdu_e1->sessionId = pdu_ue->param.pdusession_id;
    DRB_nGRAN_to_setup_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
1866 1867
    f1u_gtp_update(&UE->established_drbs[drb_f1->drb_id].f1u_tunnel_config, drb_f1);
    /* Fill E1 bearer context modification */
1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878
    fill_e1_bearer_modif(drb_e1, drb_f1);
    pdu_e1->numDRB2Modify += 1;
  }
  DevAssert(req.numPDUSessionsMod > 0);
  DevAssert(req.numPDUSessions == 0);

  // send the E1 bearer modification request message to update F1-U tunnel info
  sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
  rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}

laurent's avatar
tmp  
laurent committed
1879 1880
static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance_t instance)
{
1881
  f1ap_ue_context_setup_t *resp = &F1AP_UE_CONTEXT_SETUP_RESP(msg_p);
1882
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
1883 1884 1885 1886 1887
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
    return;
  }
1888
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1889

1890 1891 1892 1893 1894 1895 1896
  NR_CellGroupConfig_t *cellGroupConfig = NULL;
  asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                 &asn_DEF_NR_CellGroupConfig,
                                                 (void **)&cellGroupConfig,
                                                 (uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
                                                 resp->du_to_cu_rrc_information->cellGroupConfig_length);
  AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
1897

1898 1899 1900
  if (UE->masterCellGroup) {
    ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
    LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
1901
  }
1902 1903
  UE->masterCellGroup = cellGroupConfig;
  if (LOG_DEBUGFLAG(DEBUG_ASN1))
1904
    xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
1905

1906 1907 1908 1909 1910
  if (resp->drbs_to_be_setup_length > 0) {
    AssertFatal(resp->srbs_to_be_setup_length > 0 && resp->srbs_to_be_setup[0].srb_id == 2, "logic bug: we set up DRBs, so need to have both SRB1&SRB2\n");
    e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
  }

1911 1912 1913
  /* at this point, we don't have to do anything: the UE context setup request
   * includes the Security Command, whose response will trigger the following
   * messages (UE capability, to be specific) */
1914 1915
}

1916 1917 1918 1919 1920
static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p)
{
  const int instance = 0;
  f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p);
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
1921 1922 1923 1924 1925
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id);
    return;
  }
1926 1927

  /* TODO: marshall types correctly */
1928
  LOG_I(NR_RRC, "received UE Context Release Request for UE %u, forwarding to AMF\n", req->gNB_CU_ue_id);
1929 1930 1931 1932 1933 1934
  rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(instance,
                                           ue_context_p,
                                           NGAP_CAUSE_RADIO_NETWORK,
                                           NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST);
}

Robert Schmidt's avatar
Robert Schmidt committed
1935 1936 1937 1938 1939 1940 1941
static void rrc_delete_ue_data(gNB_RRC_UE_t *UE)
{
  ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
  ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
  ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults);
}

1942 1943 1944 1945 1946
static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
{
  const int instance = 0;
  f1ap_ue_context_release_complete_t *complete = &F1AP_UE_CONTEXT_RELEASE_COMPLETE(msg_p);
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
1947 1948 1949 1950 1951
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, complete->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", complete->gNB_CU_ue_id);
    return;
  }
1952 1953
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;

1954 1955 1956 1957
  /* we call nr_pdcp_remove_UE() in the handler of E1 bearer release, but if we
   * are in E1, we also need to free the UE in the CU-CP, so call it twice to
   * cover all cases */
  nr_pdcp_remove_UE(UE->rrc_ue_id);
1958
  rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, UE->rrc_ue_id);
1959
  LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti);
Robert Schmidt's avatar
Robert Schmidt committed
1960
  rrc_delete_ue_data(UE);
1961 1962 1963
  rrc_gNB_remove_ue_context(rrc, ue_context_p);
}

laurent's avatar
tmp  
laurent committed
1964 1965
static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, instance_t instance)
{
1966
  f1ap_ue_context_modif_resp_t *resp = &F1AP_UE_CONTEXT_MODIFICATION_RESP(msg_p);
1967
  protocol_ctxt_t ctxt = {.rntiMaybeUEid = resp->gNB_CU_ue_id, .module_id = instance, .instance = instance, .enb_flag = 1, .eNB_index = instance};
1968
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id];
1969 1970 1971 1972 1973
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
    return;
  }
1974
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1975

1976
  if (resp->drbs_to_be_setup_length > 0) {
1977
    e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
1978
  }
1979

1980 1981 1982 1983 1984 1985 1986 1987
  if (resp->du_to_cu_rrc_information != NULL && resp->du_to_cu_rrc_information->cellGroupConfig != NULL) {
    NR_CellGroupConfig_t *cellGroupConfig = NULL;
    asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                   &asn_DEF_NR_CellGroupConfig,
                                                   (void **)&cellGroupConfig,
                                                   (uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
                                                   resp->du_to_cu_rrc_information->cellGroupConfig_length);
    AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
matzakos's avatar
matzakos committed
1988

1989 1990 1991
    if (UE->masterCellGroup) {
      ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
      LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
1992
    }
1993
    UE->masterCellGroup = cellGroupConfig;
1994

1995
    rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p);
1996
  }
1997 1998
}

1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012
static void rrc_CU_process_ue_modification_required(MessageDef *msg_p)
{
  f1ap_ue_context_modif_required_t *required = &F1AP_UE_CONTEXT_MODIFICATION_REQUIRED(msg_p);
  protocol_ctxt_t ctxt = {.rntiMaybeUEid = required->gNB_CU_ue_id, .module_id = 0, .instance = 0, .enb_flag = 1, .eNB_index = 0};
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id];
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, required->gNB_CU_ue_id);
  if (ue_context_p == NULL) {
    LOG_E(RRC, "Could not find UE context for CU UE ID %d, cannot handle UE context modification request\n", required->gNB_CU_ue_id);
    f1ap_ue_context_modif_refuse_t refuse = {
      .gNB_CU_ue_id = required->gNB_CU_ue_id,
      .gNB_DU_ue_id = required->gNB_DU_ue_id,
      .cause = F1AP_CAUSE_RADIO_NETWORK,
      .cause_value = F1AP_CauseRadioNetwork_unknown_or_already_allocated_gnb_cu_ue_f1ap_id,
    };
Robert Schmidt's avatar
Robert Schmidt committed
2013
    rrc->mac_rrc.ue_context_modification_refuse(msg_p->ittiMsgHeader.originInstance, &refuse);
2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037
    return;
  }

  if (required->du_to_cu_rrc_information && required->du_to_cu_rrc_information->cellGroupConfig) {
    gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
    LOG_I(RRC,
          "UE Context Modification Required: new CellGroupConfig for UE ID %d/RNTI %04x, triggering reconfiguration\n",
          UE->rrc_ue_id,
          UE->rnti);

    NR_CellGroupConfig_t *cellGroupConfig = NULL;
    asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                   &asn_DEF_NR_CellGroupConfig,
                                                   (void **)&cellGroupConfig,
                                                   (uint8_t *)required->du_to_cu_rrc_information->cellGroupConfig,
                                                   required->du_to_cu_rrc_information->cellGroupConfig_length);
    if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
      LOG_E(RRC, "Cell group config decode error, refusing reconfiguration\n");
      f1ap_ue_context_modif_refuse_t refuse = {
        .gNB_CU_ue_id = required->gNB_CU_ue_id,
        .gNB_DU_ue_id = required->gNB_DU_ue_id,
        .cause = F1AP_CAUSE_PROTOCOL,
        .cause_value = F1AP_CauseProtocol_transfer_syntax_error,
      };
Robert Schmidt's avatar
Robert Schmidt committed
2038
      rrc->mac_rrc.ue_context_modification_refuse(msg_p->ittiMsgHeader.originInstance, &refuse);
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061
      return;
    }

    if (UE->masterCellGroup) {
      ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
      LOG_I(RRC, "UE %d/RNTI %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rrc_ue_id, UE->rnti);
    }
    UE->masterCellGroup = cellGroupConfig;
    if (LOG_DEBUGFLAG(DEBUG_ASN1))
      xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);

    /* trigger reconfiguration */
    nr_rrc_reconfiguration_req(ue_context_p, &ctxt, 0, 0);
    //rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p);
    //rrc_gNB_generate_defaultRRCReconfiguration(&ctxt, ue_context_p);
    return;
  }
  LOG_W(RRC,
        "nothing to be done after UE Context Modification Required for UE ID %d/RNTI %04x\n",
        required->gNB_CU_ue_id,
        required->gNB_DU_ue_id);
}

2062 2063 2064 2065
unsigned int mask_flip(unsigned int x) {
  return((((x>>8) + (x<<8))&0xffff)>>6);
}

2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076
static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
{
  pdusession_t *pdu = &pduSession->param;
  for (int i = 0; i < pdu->nb_qos; i++) {
    if (qfi == pdu->qos[i].qfi)
      return &pdu->qos[i];
  }
  AssertFatal(1 == 0, "The pdu session %d does not contain a qos flow with qfi = %d\n", pdu->pdusession_id, qfi);
  return NULL;
}

2077
/**
2078
 * @brief E1AP Bearer Context Setup Response processing on CU-CP
2079
*/
2080
void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp, instance_t instance)
2081 2082
{
  gNB_RRC_INST *rrc = RC.nrrrc[0];
2083 2084
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
  AssertFatal(ue_context_p != NULL, "did not find UE with CU UE ID %d\n", resp->gNB_cu_cp_ue_id);
2085
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
2086

2087 2088 2089 2090 2091 2092 2093 2094
  // currently: we don't have "infrastructure" to save the CU-UP UE ID, so we
  // assume (and below check) that CU-UP UE ID == CU-CP UE ID
  AssertFatal(resp->gNB_cu_cp_ue_id == resp->gNB_cu_up_ue_id,
              "cannot handle CU-UP UE ID different from CU-CP UE ID (%d vs %d)\n",
              resp->gNB_cu_cp_ue_id,
              resp->gNB_cu_up_ue_id);

  // save the tunnel address for the PDU sessions
2095
  for (int i = 0; i < resp->numPDUSessions; i++) {
2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106
    pdu_session_setup_t *e1_pdu = &resp->pduSession[i];
    rrc_pdu_session_param_t *rrc_pdu = find_pduSession(UE, e1_pdu->id, false);
    if (rrc_pdu == NULL) {
      LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id);
      continue;
    }
    rrc_pdu->param.gNB_teid_N3 = e1_pdu->teId;
    memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tlAddress, sizeof(uint8_t) * 4);
    rrc_pdu->param.gNB_addr_N3.length = sizeof(in_addr_t);
  }

2107
  /* Instruction towards the DU for DRB configuration and tunnel creation */
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144
  f1ap_drb_to_be_setup_t drbs[32]; // maximum DRB can be 32
  int nb_drb = 0;
  for (int p = 0; p < resp->numPDUSessions; ++p) {
    rrc_pdu_session_param_t *RRC_pduSession = find_pduSession(UE, resp->pduSession[p].id, false);
    DevAssert(RRC_pduSession);
    for (int i = 0; i < resp->pduSession[p].numDRBSetup; i++) {
      DRB_nGRAN_setup_t *drb_config = &resp->pduSession[p].DRBnGRanList[i];
      f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb];
      drb->drb_id = resp->pduSession[p].DRBnGRanList[i].id;
      drb->rlc_mode = rrc->configuration.um_on_default_drb ? RLC_MODE_UM : RLC_MODE_AM;
      drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tlAddress;
      drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd;
      drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].teId;
      drb->up_ul_tnl_length = 1;

      drb->nssai = RRC_pduSession->param.nssai;

      /* pass QoS info to MAC */
      int nb_qos_flows = drb_config->numQosFlowSetup;
      AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n");
      drb->drb_info.flows_to_be_setup_length = nb_qos_flows;
      drb->drb_info.flows_mapped_to_drb = calloc(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t));
      AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n");
      for (int j = 0; j < nb_qos_flows; j++) {
        drb->drb_info.flows_mapped_to_drb[j].qfi = drb_config->qosFlows[j].qfi;

        pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession);
        f1ap_qos_characteristics_t *qos_char = &drb->drb_info.flows_mapped_to_drb[j].qos_params.qos_characteristics;
        if (in_qos_char->fiveQI_type == dynamic) {
          qos_char->qos_type = dynamic;
          qos_char->dynamic.fiveqi = in_qos_char->fiveQI;
          qos_char->dynamic.qos_priority_level = in_qos_char->qos_priority;
        } else {
          qos_char->qos_type = non_dynamic;
          qos_char->non_dynamic.fiveqi = in_qos_char->fiveQI;
          qos_char->non_dynamic.qos_priority_level = in_qos_char->qos_priority;
        }
2145
      }
2146 2147 2148 2149 2150
      /* the DRB QoS parameters: we just reuse the ones from the first flow */
      drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params;

      /* pass NSSAI info to MAC */
      drb->nssai = RRC_pduSession->param.nssai;
2151

2152 2153
      nb_drb++;
    }
2154 2155 2156
  }

  /* Instruction towards the DU for SRB2 configuration */
2157
  int nb_srb = 0;
2158
  f1ap_srb_to_be_setup_t srbs[1] = {0};
2159
  if (UE->Srb[2].Active == 0) {
2160
    activate_srb(UE, 2);
2161 2162 2163 2164
    nb_srb = 1;
    srbs[0].srb_id = 2;
    srbs[0].lcid = 2;
  }
2165

2166 2167 2168
  if (!UE->as_security_active) {
    /* no AS security active, need to send UE context setup req with security
     * command (and the bearers) */
2169 2170
    protocol_ctxt_t ctxt = {.rntiMaybeUEid = UE->rrc_ue_id};
    rrc_gNB_generate_SecurityModeCommand(&ctxt, ue_context_p, nb_drb, drbs);
2171 2172 2173
    return;
  }

2174
  /* Gather UE capability if present */
2175 2176 2177 2178 2179 2180 2181 2182
  cu_to_du_rrc_information_t cu2du = {0};
  cu_to_du_rrc_information_t *cu2du_p = NULL;
  if (UE->ue_cap_buffer.len > 0 && UE->ue_cap_buffer.buf != NULL) {
    cu2du_p = &cu2du;
    cu2du.uE_CapabilityRAT_ContainerList = UE->ue_cap_buffer.buf;
    cu2du.uE_CapabilityRAT_ContainerList_length = UE->ue_cap_buffer.len;
  }

2183
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
2184
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
2185
  f1ap_ue_context_modif_req_t ue_context_modif_req = {
2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197
      .gNB_CU_ue_id = UE->rrc_ue_id,
      .gNB_DU_ue_id = ue_data.secondary_ue,
      .plmn.mcc = rrc->configuration.mcc[0],
      .plmn.mnc = rrc->configuration.mnc[0],
      .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
      .nr_cellid = rrc->nr_cellid,
      .servCellId = 0, /* TODO: correct value? */
      .srbs_to_be_setup_length = nb_srb,
      .srbs_to_be_setup = srbs,
      .drbs_to_be_setup_length = nb_drb,
      .drbs_to_be_setup = drbs,
      .cu_to_du_rrc_information = cu2du_p,
2198
  };
Robert Schmidt's avatar
Robert Schmidt committed
2199
  rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &ue_context_modif_req);
2200 2201
}

2202
/**
2203
 * @brief E1AP Bearer Context Modification Response processing on CU-CP
2204
 */
2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp)
{
  gNB_RRC_INST *rrc = RC.nrrrc[0];
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
  if (ue_context_p == NULL) {
    LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id);
    return;
  }

  // there is not really anything to do here as of now
  for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
    const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
    LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
  }
}

2221 2222
/**
 * @brief E1AP Bearer Context Release processing
2223
 */
2224 2225 2226 2227 2228 2229 2230 2231 2232
void rrc_gNB_process_e1_bearer_context_release_cplt(const e1ap_bearer_release_cplt_t *cplt)
{
  // there is not really anything to do here as of now
  // note that we don't check for the UE: it does not exist anymore if the F1
  // UE context release complete arrived from the DU first, after which we free
  // the UE context
  LOG_I(RRC, "UE %d: received bearer release complete\n", cplt->gNB_cu_cp_ue_id);
}

2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253
static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
{
  DevAssert(measresults->measResultServingMOList.list.count >= 1);
  if (measresults->measResultServingMOList.list.count > 1)
    LOG_W(RRC, "Received %d MeasResultServMO, but handling only 1!\n", measresults->measResultServingMOList.list.count);

  NR_MeasResultServMO_t *measresultservmo = measresults->measResultServingMOList.list.array[0];
  NR_MeasResultNR_t *measresultnr = &measresultservmo->measResultServingCell;
  NR_MeasQuantityResults_t *mqr = measresultnr->measResult.cellResults.resultsSSB_Cell;

  fprintf(f, "    servingCellId %ld MeasResultNR for phyCellId %ld:\n      resultSSB:", measresultservmo->servCellId, *measresultnr->physCellId);
  if (mqr != NULL) {
    const long rrsrp = *mqr->rsrp - 156;
    const float rrsrq = (float) (*mqr->rsrq - 87) / 2.0f;
    const float rsinr = (float) (*mqr->sinr - 46) / 2.0f;
    fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr);
  } else {
    fprintf(f, "NOT PROVIDED\n");
  }
}

2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283
static const char *get_rrc_connection_status_text(NR_UE_STATE_t state)
{
  switch (state) {
    case NR_RRC_INACTIVE: return "inactive";
    case NR_RRC_IDLE: return "idle";
    case NR_RRC_SI_RECEIVED: return "SI-received";
    case NR_RRC_CONNECTED: return "connected";
    case NR_RRC_RECONFIGURED: return "reconfigured";
    case NR_RRC_HO_EXECUTION: return "HO-execution";
    default: AssertFatal(false, "illegal RRC state %d\n", state); return "illegal";
  }
  return "illegal";
}

static const char *get_pdusession_status_text(pdu_session_status_t status)
{
  switch (status) {
    case PDU_SESSION_STATUS_NEW: return "new";
    case PDU_SESSION_STATUS_DONE: return "done";
    case PDU_SESSION_STATUS_ESTABLISHED: return "established";
    case PDU_SESSION_STATUS_REESTABLISHED: return "reestablished";
    case PDU_SESSION_STATUS_TOMODIFY: return "to-modify";
    case PDU_SESSION_STATUS_FAILED: return "failed";
    case PDU_SESSION_STATUS_TORELEASE: return "to-release";
    case PDU_SESSION_STATUS_RELEASED: return "released";
    default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal";
  }
  return "illegal";
}

2284 2285 2286 2287 2288 2289 2290 2291 2292
static void write_rrc_stats(const gNB_RRC_INST *rrc)
{
  const char *filename = "nrRRC_stats.log";
  FILE *f = fopen(filename, "w");
  if (f == NULL) {
    LOG_E(NR_RRC, "cannot open %s for writing\n", filename);
    return;
  }

2293
  int i = 0;
2294 2295
  rrc_gNB_ue_context_t *ue_context_p = NULL;
  /* cast is necessary to eliminate warning "discards ‘const’ qualifier" */
2296 2297
  RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &((gNB_RRC_INST *)rrc)->rrc_ue_head)
  {
2298
    const gNB_RRC_UE_t *ue_ctxt = &ue_context_p->ue_context;
2299
    f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_ctxt->rrc_ue_id);
2300

2301 2302 2303 2304 2305 2306 2307
    fprintf(f,
            "UE %d CU UE ID %d DU UE ID %d RNTI %04x random identity %016lx",
            i,
            ue_ctxt->rrc_ue_id,
            ue_data.secondary_ue,
            ue_ctxt->rnti,
            ue_ctxt->random_ue_identity);
2308
    if (ue_ctxt->Initialue_identity_5g_s_TMSI.presence)
2309
      fprintf(f, " S-TMSI %x", ue_ctxt->Initialue_identity_5g_s_TMSI.fiveg_tmsi);
2310
    fprintf(f, ":\n");
2311

2312 2313 2314 2315 2316 2317 2318 2319
    fprintf(f, "    RRC status %s\n", get_rrc_connection_status_text(ue_ctxt->StatusRrc));

    if (ue_ctxt->nb_of_pdusessions == 0)
      fprintf(f, "    (no PDU sessions)\n");
    for (int nb_pdu = 0; nb_pdu < ue_ctxt->nb_of_pdusessions; ++nb_pdu) {
      const rrc_pdu_session_param_t *pdu = &ue_ctxt->pduSession[nb_pdu];
      fprintf(f, "    PDU session %d ID %d status %s\n", nb_pdu, pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
    }
2320

2321 2322 2323 2324 2325 2326 2327 2328
    fprintf(f, "    associated DU: ");
    if (ue_data.du_assoc_id == -1)
      fprintf(f, " (local/integrated CU-DU)");
    else if (ue_data.du_assoc_id == 0)
      fprintf(f, " DU offline/unavailable");
    else
      fprintf(f, " DU assoc ID %d", ue_data.du_assoc_id);
    fprintf(f, "\n");
2329 2330 2331

    if (ue_ctxt->measResults)
      print_rrc_meas(f, ue_ctxt->measResults);
2332
    ++i;
2333 2334
  }

2335 2336 2337
  fprintf(f, "\n");
  dump_du_info(rrc, f);

2338 2339 2340
  fclose(f);
}

2341
void *rrc_gnb_task(void *args_p) {
laurent's avatar
tmp  
laurent committed
2342
  MessageDef *msg_p;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2343 2344
  instance_t                         instance;
  int                                result;
2345
  protocol_ctxt_t ctxt = {.module_id = 0, .enb_flag = 1, .instance = 0, .rntiMaybeUEid = 0, .frame = -1, .subframe = -1, .eNB_index = 0, .brOption = false};
2346 2347

  long stats_timer_id = 1;
2348 2349 2350 2351 2352
  if (!IS_SOFTMODEM_NOSTATS_BIT) {
    /* timer to write stats to file */
    timer_setup(1, 0, TASK_RRC_GNB, 0, TIMER_PERIODIC, NULL, &stats_timer_id);
  }
  
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2353 2354
  itti_mark_task_ready(TASK_RRC_GNB);
  LOG_I(NR_RRC,"Entering main loop of NR_RRC message task\n");
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2355 2356

  while (1) {
2357
    // Wait for a message
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2358
    itti_receive_msg(TASK_RRC_GNB, &msg_p);
laurent's avatar
tmp  
laurent committed
2359
    const char *msg_name_p = ITTI_MSG_NAME(msg_p);
2360
    instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
2361 2362 2363 2364 2365
    LOG_D(NR_RRC,
          "RRC GNB Task Received %s for instance %ld from task %s\n",
          ITTI_MSG_NAME(msg_p),
          ITTI_MSG_DESTINATION_INSTANCE(msg_p),
          ITTI_MSG_ORIGIN_NAME(msg_p));
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2366
    switch (ITTI_MSG_ID(msg_p)) {
2367
      case TERMINATE_MESSAGE:
Xue Song's avatar
Xue Song committed
2368
        LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n");
2369 2370
        itti_exit_task();
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2371

2372
      case MESSAGE_TEST:
2373
        LOG_I(NR_RRC, "[gNB %ld] Received %s\n", instance, msg_name_p);
2374
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2375

2376
      case TIMER_HAS_EXPIRED:
2377 2378 2379 2380
        if (TIMER_HAS_EXPIRED(msg_p).timer_id == stats_timer_id)
          write_rrc_stats(RC.nrrrc[0]);
        else
          itti_send_msg_to_task(TASK_RRC_GNB, 0, TIMER_HAS_EXPIRED(msg_p).arg); /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */
2381 2382
        break;

2383 2384 2385
      case F1AP_INITIAL_UL_RRC_MESSAGE:
        AssertFatal(NODE_IS_CU(RC.nrrrc[instance]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[instance]->node_type),
                    "should not receive F1AP_INITIAL_UL_RRC_MESSAGE, need call by CU!\n");
Robert Schmidt's avatar
Robert Schmidt committed
2386
        rrc_gNB_process_initial_ul_rrc_message(msg_p->ittiMsgHeader.originInstance, &F1AP_INITIAL_UL_RRC_MESSAGE(msg_p));
2387
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2388 2389

      /* Messages from PDCP */
2390
      /* From DU -> CU */
2391
      case F1AP_UL_RRC_MESSAGE:
2392 2393 2394
        PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
                                      instance,
                                      GNB_FLAG_YES,
2395
                                      F1AP_UL_RRC_MESSAGE(msg_p).gNB_CU_ue_id,
2396 2397
                                      0,
                                      0);
2398 2399 2400 2401 2402 2403 2404
        LOG_D(NR_RRC,
              "Decoding DCCH %d: ue %04lx, inst %ld, ctxt %p, size %d\n",
              F1AP_UL_RRC_MESSAGE(msg_p).srb_id,
              ctxt.rntiMaybeUEid,
              instance,
              &ctxt,
              F1AP_UL_RRC_MESSAGE(msg_p).rrc_container_length);
Xue Song's avatar
Xue Song committed
2405
        rrc_gNB_decode_dcch(&ctxt,
2406 2407 2408 2409
                            F1AP_UL_RRC_MESSAGE(msg_p).srb_id,
                            F1AP_UL_RRC_MESSAGE(msg_p).rrc_container,
                            F1AP_UL_RRC_MESSAGE(msg_p).rrc_container_length);
        free(F1AP_UL_RRC_MESSAGE(msg_p).rrc_container);
Xue Song's avatar
Xue Song committed
2410
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2411

Xue Song's avatar
Xue Song committed
2412
      case NGAP_DOWNLINK_NAS:
laurent's avatar
tmp  
laurent committed
2413
        rrc_gNB_process_NGAP_DOWNLINK_NAS(msg_p, instance, &rrc_gNB_mui);
Xue Song's avatar
Xue Song committed
2414 2415
        break;

2416
      case NGAP_PDUSESSION_SETUP_REQ:
laurent's avatar
tmp  
laurent committed
2417
        rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance);
2418 2419
        break;

2420
      case NGAP_PDUSESSION_MODIFY_REQ:
laurent's avatar
tmp  
laurent committed
2421
        rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(msg_p, instance);
2422 2423 2424
        break;

      case NGAP_PDUSESSION_RELEASE_COMMAND:
laurent's avatar
tmp  
laurent committed
2425
        rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
2426 2427
        break;

Xue Song's avatar
Xue Song committed
2428 2429
      /* Messages from F1AP task */
      case F1AP_SETUP_REQ:
2430
        AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
2431
        rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p), msg_p->ittiMsgHeader.originInstance);
Xue Song's avatar
Xue Song committed
2432
        break;
2433

2434
      case F1AP_UE_CONTEXT_SETUP_RESP:
laurent's avatar
tmp  
laurent committed
2435
        rrc_CU_process_ue_context_setup_response(msg_p, instance);
2436
        break;
2437

2438
      case F1AP_UE_CONTEXT_MODIFICATION_RESP:
laurent's avatar
tmp  
laurent committed
2439
        rrc_CU_process_ue_context_modification_response(msg_p, instance);
2440 2441
        break;

2442 2443 2444 2445
      case F1AP_UE_CONTEXT_MODIFICATION_REQUIRED:
        rrc_CU_process_ue_modification_required(msg_p);
        break;

2446
      case F1AP_UE_CONTEXT_RELEASE_REQ:
2447
        rrc_CU_process_ue_context_release_request(msg_p);
2448 2449 2450 2451
        break;

      case F1AP_UE_CONTEXT_RELEASE_COMPLETE:
        rrc_CU_process_ue_context_release_complete(msg_p);
2452
        break;
Xue Song's avatar
Xue Song committed
2453

2454 2455 2456 2457
      case F1AP_LOST_CONNECTION:
        rrc_CU_process_f1_lost_connection(RC.nrrrc[0], &F1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
        break;

2458
      /* Messages from X2AP */
2459
      case X2AP_ENDC_SGNB_ADDITION_REQ:
2460
        LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n");
2461
        rrc_gNB_process_AdditionRequestInformation(instance, &X2AP_ENDC_SGNB_ADDITION_REQ(msg_p));
2462
        break;
2463

2464
      case X2AP_ENDC_SGNB_RECONF_COMPLETE:
Melissa Elkadi's avatar
Melissa Elkadi committed
2465
        LOG_A(NR_RRC, "Handling of reconfiguration complete message at RRC gNB is pending \n");
2466
        break;
Xue Song's avatar
Xue Song committed
2467

2468
      case NGAP_INITIAL_CONTEXT_SETUP_REQ:
laurent's avatar
tmp  
laurent committed
2469
        rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p, instance);
yaojie's avatar
yaojie committed
2470
        break;
2471

Cedric Roux's avatar
Cedric Roux committed
2472 2473
      case X2AP_ENDC_SGNB_RELEASE_REQUEST:
        LOG_I(NR_RRC, "Received ENDC sgNB release request from X2AP \n");
2474
        rrc_gNB_process_release_request(instance, &X2AP_ENDC_SGNB_RELEASE_REQUEST(msg_p));
Cedric Roux's avatar
Cedric Roux committed
2475 2476 2477
        break;

      case X2AP_ENDC_DC_OVERALL_TIMEOUT:
2478
        rrc_gNB_process_dc_overall_timeout(instance, &X2AP_ENDC_DC_OVERALL_TIMEOUT(msg_p));
Cedric Roux's avatar
Cedric Roux committed
2479 2480
        break;

2481
      case NGAP_UE_CONTEXT_RELEASE_REQ:
laurent's avatar
tmp  
laurent committed
2482
        rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(msg_p, instance);
2483 2484 2485
        break;

      case NGAP_UE_CONTEXT_RELEASE_COMMAND:
laurent's avatar
tmp  
laurent committed
2486
        rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p, instance);
2487 2488
        break;

2489
      case E1AP_SETUP_REQ:
2490
        rrc_gNB_process_e1_setup_req(msg_p->ittiMsgHeader.originInstance, &E1AP_SETUP_REQ(msg_p));
2491 2492 2493 2494
        break;

      case E1AP_BEARER_CONTEXT_SETUP_RESP:
        rrc_gNB_process_e1_bearer_context_setup_resp(&E1AP_BEARER_CONTEXT_SETUP_RESP(msg_p), instance);
2495
        break;
2496

2497 2498 2499 2500
      case E1AP_BEARER_CONTEXT_MODIFICATION_RESP:
        rrc_gNB_process_e1_bearer_context_modif_resp(&E1AP_BEARER_CONTEXT_MODIFICATION_RESP(msg_p));
        break;

2501 2502 2503 2504
      case E1AP_BEARER_CONTEXT_RELEASE_CPLT:
        rrc_gNB_process_e1_bearer_context_release_cplt(&E1AP_BEARER_CONTEXT_RELEASE_CPLT(msg_p));
        break;

2505 2506 2507 2508
      case E1AP_LOST_CONNECTION: /* CUCP */
        rrc_gNB_process_e1_lost_connection(RC.nrrrc[0], &E1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
        break;

2509
      case NGAP_PAGING_IND:
laurent's avatar
tmp  
laurent committed
2510
        rrc_gNB_process_PAGING_IND(msg_p, instance);
2511 2512
        break;

2513
      default:
2514
        LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
2515
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2516 2517 2518 2519 2520 2521 2522
    }

    result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
    AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
    msg_p = NULL;
  }
}
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2523

2524 2525 2526
typedef struct deliver_ue_ctxt_setup_data_t {
  gNB_RRC_INST *rrc;
  f1ap_ue_context_setup_t *setup_req;
Robert Schmidt's avatar
Robert Schmidt committed
2527
  sctp_assoc_t assoc_id;
2528
} deliver_ue_ctxt_setup_data_t;
2529 2530 2531
static void rrc_deliver_ue_ctxt_setup_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
  DevAssert(deliver_pdu_data != NULL);
2532 2533 2534
  deliver_ue_ctxt_setup_data_t *data = deliver_pdu_data;
  data->setup_req->rrc_container = (uint8_t*)buf;
  data->setup_req->rrc_container_length = size;
Robert Schmidt's avatar
Robert Schmidt committed
2535
  data->rrc->mac_rrc.ue_context_setup_request(data->assoc_id, data->setup_req);
2536 2537
}

heshanyun's avatar
heshanyun committed
2538 2539 2540 2541
//-----------------------------------------------------------------------------
void
rrc_gNB_generate_SecurityModeCommand(
  const protocol_ctxt_t *const ctxt_pP,
2542 2543 2544
  rrc_gNB_ue_context_t  *const ue_context_pP,
  int n_drbs,
  const f1ap_drb_to_be_setup_t *drbs
heshanyun's avatar
heshanyun committed
2545 2546 2547 2548 2549
)
//-----------------------------------------------------------------------------
{
  uint8_t                             buffer[100];
  uint8_t                             size;
2550
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
2551
  AssertFatal(!ue_p->as_security_active, "logic error: security already active\n");
2552

2553
  T(T_ENB_RRC_SECURITY_MODE_COMMAND, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rntiMaybeUEid));
2554
  NR_IntegrityProtAlgorithm_t integrity_algorithm = (NR_IntegrityProtAlgorithm_t)ue_p->integrity_algorithm;
2555
  size = do_NR_SecurityModeCommand(ctxt_pP, buffer, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), ue_p->ciphering_algorithm, integrity_algorithm);
heshanyun's avatar
heshanyun committed
2556
  LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Security Mode Command\n");
2557
  LOG_I(NR_RRC, "UE %u Logical Channel DL-DCCH, Generate SecurityModeCommand (bytes %d)\n", ue_p->rrc_ue_id, size);
2558

2559 2560
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
2561

2562 2563 2564 2565 2566 2567 2568 2569
  cu_to_du_rrc_information_t cu2du = {0};
  cu_to_du_rrc_information_t *cu2du_p = NULL;
  if (ue_p->ue_cap_buffer.len > 0 && ue_p->ue_cap_buffer.buf != NULL) {
    cu2du_p = &cu2du;
    cu2du.uE_CapabilityRAT_ContainerList = ue_p->ue_cap_buffer.buf;
    cu2du.uE_CapabilityRAT_ContainerList_length = ue_p->ue_cap_buffer.len;
  }

2570 2571 2572 2573 2574 2575 2576 2577 2578 2579
  int nb_srb = 0;
  f1ap_srb_to_be_setup_t srb_buf[1] = {0};
  f1ap_srb_to_be_setup_t *srbs = 0;
  if (n_drbs > 0) {
    nb_srb = 1;
    srb_buf[0].srb_id = 2;
    srb_buf[0].lcid = 2;
    srbs = srb_buf;
  }

2580
  /* the callback will fill the UE context setup request and forward it */
2581
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
2582
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
2583
  f1ap_ue_context_setup_t ue_context_setup_req = {
2584 2585 2586 2587 2588 2589 2590
      .gNB_CU_ue_id = ue_p->rrc_ue_id,
      .gNB_DU_ue_id = ue_data.secondary_ue,
      .plmn.mcc = rrc->configuration.mcc[0],
      .plmn.mnc = rrc->configuration.mnc[0],
      .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
      .nr_cellid = rrc->nr_cellid,
      .servCellId = 0, /* TODO: correct value? */
2591 2592 2593 2594
      .srbs_to_be_setup_length = nb_srb,
      .srbs_to_be_setup = srbs,
      .drbs_to_be_setup_length = n_drbs,
      .drbs_to_be_setup = (f1ap_drb_to_be_setup_t *) drbs,
2595
      .cu_to_du_rrc_information = cu2du_p,
2596
  };
Robert Schmidt's avatar
Robert Schmidt committed
2597
  deliver_ue_ctxt_setup_data_t data = {.rrc = rrc, .setup_req = &ue_context_setup_req, .assoc_id = ue_data.du_assoc_id };
2598
  nr_pdcp_data_req_srb(ctxt_pP->rntiMaybeUEid, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_setup_req, &data);
heshanyun's avatar
heshanyun committed
2599 2600
}

zhanghuiqin's avatar
zhanghuiqin committed
2601 2602 2603 2604 2605 2606 2607 2608 2609
void
rrc_gNB_generate_UECapabilityEnquiry(
  const protocol_ctxt_t *const ctxt_pP,
  rrc_gNB_ue_context_t          *const ue_context_pP
)
//-----------------------------------------------------------------------------
{
  uint8_t                             buffer[100];
  uint8_t                             size;
2610

2611
  T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rntiMaybeUEid));
2612 2613 2614 2615
  gNB_RRC_UE_t *ue = &ue_context_pP->ue_context;
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id);
  ue->xids[xid] = RRC_UECAPABILITY_ENQUIRY;
  size = do_NR_SA_UECapabilityEnquiry(ctxt_pP, buffer, xid);
2616
  LOG_I(NR_RRC, "UE %d: Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d, xid %d)\n", ue->rrc_ue_id, size, xid);
2617

2618 2619
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
2620

2621
  nr_rrc_transfer_protected_rrc_message(rrc, ue, DCCH, buffer, size);
zhanghuiqin's avatar
zhanghuiqin committed
2622
}
heshanyun's avatar
heshanyun committed
2623

2624 2625 2626
typedef struct deliver_ue_ctxt_release_data_t {
  gNB_RRC_INST *rrc;
  f1ap_ue_context_release_cmd_t *release_cmd;
Robert Schmidt's avatar
Robert Schmidt committed
2627
  sctp_assoc_t assoc_id;
2628
} deliver_ue_ctxt_release_data_t;
2629 2630 2631
static void rrc_deliver_ue_ctxt_release_cmd(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
  DevAssert(deliver_pdu_data != NULL);
2632 2633 2634
  deliver_ue_ctxt_release_data_t *data = deliver_pdu_data;
  data->release_cmd->rrc_container = (uint8_t*) buf;
  data->release_cmd->rrc_container_length = size;
Robert Schmidt's avatar
Robert Schmidt committed
2635
  data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd);
2636 2637
}

heshanyun's avatar
heshanyun committed
2638 2639 2640 2641 2642 2643
//-----------------------------------------------------------------------------
/*
* Generate the RRC Connection Release to UE.
* If received, UE should switch to RRC_IDLE mode.
*/
void
Xue Song's avatar
Xue Song committed
2644
rrc_gNB_generate_RRCRelease(
heshanyun's avatar
heshanyun committed
2645
  const protocol_ctxt_t *const ctxt_pP,
Xue Song's avatar
Xue Song committed
2646
  rrc_gNB_ue_context_t  *const ue_context_pP
heshanyun's avatar
heshanyun committed
2647 2648 2649
)
//-----------------------------------------------------------------------------
{
2650 2651
  uint8_t buffer[RRC_BUF_SIZE] = {0};
  int size = do_NR_RRCRelease(buffer, RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id));
2652

heshanyun's avatar
heshanyun committed
2653
  LOG_I(NR_RRC,
Xue Song's avatar
Xue Song committed
2654 2655
        PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate RRCRelease (bytes %d)\n",
        PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),
heshanyun's avatar
heshanyun committed
2656 2657
        size);

2658
  gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
2659
  const gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
2660
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
2661
  RETURN_IF_INVALID_ASSOC_ID(ue_data);
2662
  f1ap_ue_context_release_cmd_t ue_context_release_cmd = {
2663
    .gNB_CU_ue_id = UE->rrc_ue_id,
2664 2665 2666 2667 2668
    .gNB_DU_ue_id = ue_data.secondary_ue,
    .cause = F1AP_CAUSE_RADIO_NETWORK,
    .cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release
    .srb_id = DCCH,
  };
Robert Schmidt's avatar
Robert Schmidt committed
2669
  deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd, .assoc_id = ue_data.du_assoc_id};
2670
  nr_pdcp_data_req_srb(ctxt_pP->rntiMaybeUEid, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data);
2671

2672 2673 2674 2675 2676 2677 2678 2679 2680 2681
  /* a UE might not be associated to a CU-UP if it never requested a PDU
   * session (intentionally, or because of erros) */
  if (ue_associated_to_cuup(rrc, UE)) {
    sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
    e1ap_bearer_release_cmd_t cmd = {
      .gNB_cu_cp_ue_id = UE->rrc_ue_id,
      .gNB_cu_up_ue_id = UE->rrc_ue_id,
    };
    rrc->cucp_cuup.bearer_context_release(assoc_id, &cmd);
  }
2682

2683 2684 2685 2686
#ifdef E2_AGENT
  signal_rrc_state_changed_to(UE, RC_SM_RRC_IDLE);
#endif

2687
  /* UE will be freed after UE context release complete */
heshanyun's avatar
heshanyun committed
2688
}
2689

2690 2691 2692
int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib1, uint32_t tmsi, uint8_t paging_drx)
{
  instance_t instance = 0;
2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759
  const unsigned int Ttab[4] = {32,64,128,256};
  uint8_t Tc;
  uint8_t Tue;
  uint32_t pfoffset;
  uint32_t N;  /* N: min(T,nB). total count of PF in one DRX cycle */
  uint32_t Ns = 0;  /* Ns: max(1,nB/T) */
  uint32_t T;  /* DRX cycle */
  uint32_t length;
  uint8_t buffer[RRC_BUF_SIZE];

  /* get default DRX cycle from configuration */
  Tc = sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.defaultPagingCycle;

  Tue = paging_drx;
  /* set T = min(Tc,Tue) */
  T = Tc < Tue ? Ttab[Tc] : Ttab[Tue];
  /* set N = PCCH-Config->nAndPagingFrameOffset */
  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present) {
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneT:
      N = T;
      pfoffset = 0;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_halfT:
      N = T/2;
      pfoffset = 1;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_quarterT:
      N = T/4;
      pfoffset = 3;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneEighthT:
      N = T/8;
      pfoffset = 7;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneSixteenthT:
      N = T/16;
      pfoffset = 15;
      break;
    default:
      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  pfoffset error (pfoffset %d)\n",
            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present);
      return (-1);

  }

  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns) {
    case NR_PCCH_Config__ns_four:
      if(*sib1->servingCellConfigCommon->downlinkConfigCommon.initialDownlinkBWP.pdcch_ConfigCommon->choice.setup->pagingSearchSpace == 0){
        LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  ns error only 1 or 2 is allowed when pagingSearchSpace is 0\n",
              instance);
        return (-1);
      } else {
        Ns = 4;
      }
      break;
    case NR_PCCH_Config__ns_two:
      Ns = 2;
      break;
    case NR_PCCH_Config__ns_one:
      Ns = 1;
      break;
    default:
      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: ns error (ns %ld)\n",
            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns);
      return (-1);
  }

2760 2761 2762
  (void) N; /* not used, suppress warning */
  (void) Ns; /* not used, suppress warning */
  (void) pfoffset; /* not used, suppress warning */
2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773

  /* Create message for PDCP (DLInformationTransfer_t) */
  length = do_NR_Paging (instance,
                         buffer,
                         tmsi);

  if (length == -1) {
    LOG_I(NR_RRC, "do_Paging error\n");
    return -1;
  }
  // TODO, send message to pdcp
2774
  (void) assoc_id;
2775 2776 2777

  return 0;
}